Compare commits

..

5 Commits

Author SHA1 Message Date
moon
997aa33d98 settlement detail changed 2026-07-21 14:46:31 +06:30
moon
152833ed4e auto settlement and settlement receipt fixed 2026-07-20 22:07:50 +06:30
moon
0c2ae391c3 e-receipt settlement 2026-07-20 12:02:06 +06:30
moon
e613dfa246 auto-route 2026-07-20 10:18:34 +06:30
moon
2f1466d67b new e-receipt settlement request 2026-07-18 23:43:28 +06:30
19 changed files with 435 additions and 723 deletions

View File

@ -2,7 +2,7 @@
## [2.12] - 06-05-2026
- **Pinpad Sound Toggle**
- **Pin pad Sound Toggle**
- Beep is ON by default
- Can Toggle PinPad sound on Side Navigation ( Under SystemManagement Tab)
@ -33,4 +33,29 @@
- signature re-show on void
- signature re-show on refund
- pre-auth, pre-auth can, pre-auth com s' manual entry flow are changed
- can show/hide manual entry in all transactions
- can show/hide manual entry in all transactions
## [2.35] - 29-05-2026
- app crash fix
## [2.36] - 29-05-2026
- local database migration
## [2.37] - 06-07-2026
- **General**
- E-receipt send to backend service
- One Click Settlement
## [2.38] - 06-07-2026
- **General**
- SETTLEMENT both in one click
- showing each transaction detail when SETTLE
-
## [2.39] - 19-07-2026
- **General**
- New E-receipt for settlement
## [2.39] -
- **General**
- show total for each currency for duel currency
- void, pre-auth, cash adv, refund can now control from TMS for paper receipt

View File

@ -14,8 +14,8 @@ android {
applicationId "com.utsmm.kbz"
minSdk 24
targetSdk 33
versionCode 47
versionName "2.38"
versionCode 44
versionName "2.39"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -30,6 +30,8 @@ android {
applicationId "com.utsmm.kbz.sit"
versionNameSuffix "-sit"
resValue "string", "app_name", "KBZ-SIT"
versionCode 31
versionName "2.11"
}
uat {
@ -37,6 +39,8 @@ android {
applicationId "com.utsmm.kbz.uat"
versionNameSuffix "-uat"
resValue "string", "app_name", "KBZ-UAT"
versionCode 44
versionName "2.39"
}
prod {
@ -44,6 +48,8 @@ android {
applicationId "com.utsmm.kbz"
versionNameSuffix ""
resValue "string", "app_name", "KBZ-POS"
versionCode 6
versionName "2.3.1"
}
}

View File

@ -49,6 +49,7 @@ import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsSettings;
import com.utsmyanmar.paylibs.utils.enums.TransMenu;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.util.enums.SettlementType;
import com.utsmm.kbz.databinding.ActivityMainNewBinding;
import com.utsmm.kbz.service.AutoAlarmReceiver;
import com.utsmm.kbz.ui.core_ui.InputPasswordFragment;
@ -474,92 +475,21 @@ public class MainActivity extends AppCompatActivity implements
private void handleAutoSettlementIntent(Intent intent) {
if (intent == null) return;
boolean hasPOS = intent.getBooleanExtra("AUTO_SETTLEMENT", false);
boolean hasQR = intent.getBooleanExtra("AUTO_QR_SETTLEMENT", false);
boolean hasAutoSettle = intent.getBooleanExtra("AUTO_SETTLEMENT", false);
if (!hasAutoSettle) return;
if (!hasPOS && !hasQR) return;
// Clear immediately so onResume won't re-trigger on next wake
intent.removeExtra("AUTO_SETTLEMENT");
intent.removeExtra("AUTO_QR_SETTLEMENT");
if (hasPOS) {
PayDetail payDetail = (PayDetail) intent.getSerializableExtra("EXTRA_PAY_DETAIL");
if (payDetail != null) {
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.transactionsType.setValue(TransactionsType.SETTLEMENT);
sharedViewModel.setAmount(POSUtil.getInstance().getDecimalAmountSeparatorFormat(payDetail.getAmount()));
try {
navController.navigate(R.id.transactionResultFragment);
} catch (Exception e) {
LogUtil.e(TAG, "Navigation error: " + e.getMessage());
}
// If QR also exists, trigger it AFTER POS result is done
if (hasQR) {
navigateToQRSettlementAfterPOS(intent);
}
} else {
// POS had no data, go straight to QR if available
if (hasQR) {
handleQRSettlement(intent);
}
}
} else if (hasQR) {
// Only QR settlement, no POS
handleQRSettlement(intent);
}
}
private void navigateToQRSettlementAfterPOS(Intent intent) {
// Observe transactionResultFragment's completion via ViewModel
// When POS result screen signals "done" (print complete / user dismissed),
// then navigate to QR result
sharedViewModel.onSettlementResultDone.observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean done) {
if (done != null && done) {
// Remove observer so it only fires once
sharedViewModel.onSettlementResultDone.removeObserver(this);
// Reset flag
sharedViewModel.onSettlementResultDone.postValue(false);
// Now navigate to QR
handleQRSettlement(intent);
}
}
});
}
private void handleQRSettlement(Intent intent) {
int qrSaleCount = intent.getIntExtra("QR_SALE_COUNT", 0);
long qrSaleAmount = intent.getLongExtra("QR_SALE_AMOUNT", 0L);
int qrRefundCount = intent.getIntExtra("QR_REFUND_COUNT", 0);
long qrRefundAmount = intent.getLongExtra("QR_REFUND_AMOUNT", 0L);
long totalAmount = intent.getLongExtra("QR_TOTAL_AMOUNT", 0L);
List<PayDetail> qrTransList = (List<PayDetail>) intent.getSerializableExtra("QR_TRANS_LIST");
SettleData settleData = new SettleData(
qrSaleCount, qrSaleAmount, 0, 0L, qrRefundCount, qrRefundAmount, 0, 0L);
TradeData tradeData = Params.newTrade(false);
PayDetail payDetail = tradeData.getPayDetail();
payDetail.setSettleDataObj(settleData);
payDetail.setTransactionType(TransactionsType.MMQR_SETTLEMENT.value);
payDetail.setTransType(TransactionsType.MMQR_SETTLEMENT.name);
payDetail.setAmount(totalAmount);
payDetail.setTradeAnswerCode("000");
sharedViewModel.setAmount(POSUtil.getInstance().getDecimalAmountSeparatorFormat(totalAmount));
sharedViewModel.payDetails.setValue(qrTransList);
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.transactionsType.setValue(TransactionsType.MMQR_SETTLEMENT);
sharedViewModel.settlementType.setValue(SettlementType.NORMAL);
sharedViewModel.transactionsType.setValue(TransactionsType.SETTLEMENT);
sharedViewModel.setTransMenu(TransMenu.SETTLEMENT);
sharedViewModel.autoSettle.setValue(true);
try {
navController.navigate(R.id.transactionResultFragment);
navController.navigate(R.id.settlementTransactionFragment);
} catch (Exception e) {
LogUtil.e(TAG, "QR Auto Settlement navigation error: " + e.getMessage());
LogUtil.e(TAG, "Navigation error: " + e.getMessage());
}
}

View File

@ -1,180 +1,68 @@
package com.utsmm.kbz.service;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.gson.Gson;
import com.utsmm.kbz.MainActivity;
import com.utsmm.kbz.util.EReceiptUtil;
import com.utsmm.kbz.util.enums.TransactionStatus;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptRequest;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmm.kbz.BuildConfig;
import com.utsmm.kbz.R;
import com.utsmm.kbz.ui.settlement.SettlementViewModel;
import javax.inject.Inject;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import dagger.hilt.android.AndroidEntryPoint;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.schedulers.Schedulers;
import retrofit2.HttpException;
import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.batch_upload.BatchListener;
import com.utsmyanmar.paylibs.batch_upload.BatchUploadProcess;
import com.utsmyanmar.paylibs.isobuilder.ISOMode;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOMsgX;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOVersion;
import com.utsmyanmar.paylibs.model.CardSettleData;
import com.utsmyanmar.paylibs.model.MsgField;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.QRSettleData;
import com.utsmyanmar.paylibs.model.SettleData;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.network.ISOCallback;
import com.utsmyanmar.paylibs.network.ISOSocket;
import com.utsmyanmar.paylibs.sign_on.EchoTestProcess;
import com.utsmyanmar.paylibs.sign_on.SignOnListener;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.MessageType;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.HostName;
import com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionType;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmyanmar.paylibs.utils.params.Params;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@AndroidEntryPoint
public class AutoSettleService extends Service {
private static final String TAG = AutoSettleService.class.getSimpleName();
public static final String ACTION_DATA_RECEIVED = BuildConfig.APPLICATION_ID + ".ACTION_DATA_RECEIVED";
public static final String EXTRA_DATA = BuildConfig.APPLICATION_ID + ".EXTRA_DATA";
private Handler handler;
private SettlementViewModel settlementViewModel;
// Flags to prevent infinite loops
private boolean regularSettlementCompleted = false;
private boolean qrSettlementCompleted = false;
private boolean posHasData = false; // true = POS actually ran and has result
private boolean qrHasData = false; // true = QR actually ran and has result
// Observers to keep reference for cleanup
private Observer<List<PayDetail>> regularSettlementObserver;
private Observer<List<PayDetail>> qrSettlementObserver;
private int i = 0;
private boolean flag = false;
@Inject
Repository repository;
private PayDetail pendingPayDetail = null;
private Bundle pendingQrData = null;
private boolean posSettlementReady = false;
private boolean qrSettlementReady = false;
ArrayList<PayDetail> payDetails = new ArrayList<>();
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default";
private void startForegroundService() {
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Create notification channel for Android 8+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
"Auto Settlement Service",
NotificationManager.IMPORTANCE_LOW // Use LOW importance for foreground service
NotificationManager.IMPORTANCE_LOW
);
channel.setDescription("Auto settlement background service");
notificationManager.createNotificationChannel(channel);
}
// Create foreground notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Auto Settlement Active")
.setContentText("Processing scheduled settlement...")
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true) // Make it non-dismissible
.setOngoing(false)
.setAutoCancel(false);
// Start foreground service
startForeground(1002, builder.build());
}
private void createNotification() {
NotificationManager mNotificationManager = (NotificationManager)getSystemService( NOTIFICATION_SERVICE ) ;
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext() , default_notification_channel_id ) ;
mBuilder.setContentTitle( "Auto Settlement Running" ) ;
mBuilder.setContentText( "Processing transactions..." ) ;
mBuilder.setTicker( "Auto Settlement Service" ) ;
mBuilder.setSmallIcon(R.drawable. ic_launcher_foreground ) ;
mBuilder.setAutoCancel( true ) ;
if (android.os.Build.VERSION. SDK_INT >= android.os.Build.VERSION_CODES. O ) {
int importance = NotificationManager. IMPORTANCE_HIGH ;
NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID , "AUTO_SETTLEMENT_CHANNEL" , importance) ;
mBuilder.setChannelId( NOTIFICATION_CHANNEL_ID ) ;
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(notificationChannel) ;
}
assert mNotificationManager != null;
mNotificationManager.notify(( int ) System. currentTimeMillis () , mBuilder.build()) ;
}
private void sendDataToViewModel(String data) {
Intent intent = new Intent(ACTION_DATA_RECEIVED);
intent.putExtra(EXTRA_DATA, data);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@SuppressLint("CheckResult")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.d(TAG, "<<<<<<<<<<<<<<< Auto Settlement Service Started >>>>>>>>>>>>>>>>.");
try {
// Start as foreground service for Android 8+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
startForegroundService();
}
sendDataToViewModel("Auto settlement initiated");
// Also create regular notification
createNotification();
if (SystemParamsOperation.getInstance().getSettlementStatus()) {
performSettlement();
// regularSettlementCompleted = true;
performQRSettlement();
launchSettlementFragment();
} else {
LogUtil.d(TAG, "Settlement is disabled in system parameters");
stopSelf();
@ -188,438 +76,13 @@ public class AutoSettleService extends Service {
return START_NOT_STICKY;
}
private void performSettlement() {
if (regularSettlementCompleted) {
return;
}
final ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder(
ISOVersion.VERSION_1993,
ISOMode.BOTH_HEADER_TPDU,
HostName.BPC
).build();
regularSettlementObserver = new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> list) {
if (regularSettlementCompleted) {
return;
}
payDetails.clear();
payDetails.addAll(list);
regularSettlementCompleted = true;
repository.getSettlementPOS().removeObserver(this);
int saleCount = 0;
long saleAmount = 0L;
int preCount = 0;
long preAmount = 0L;
int refundCount = 0;
long refundAmount = 0L;
int caCount = 0;
long caAmount = 0L;
if (list != null && !list.isEmpty()) {
for (PayDetail pay : list) {
if (pay.getTransactionType() == TransactionType.SALE && !pay.isCanceled) {
saleCount++;
saleAmount += pay.getAmount();
} else if (pay.getTransactionType() == TransactionType.PRE_SALE_COMPLETE && !pay.isCanceled) {
preCount++;
preAmount += pay.getAmount();
} else if (pay.getTransactionType() == TransactionType.REFUND) {
refundCount++;
refundAmount += pay.getAmount();
} else if (pay.getTransactionType() == TransactionType.CASH_ADVANCE) {
caCount++;
caAmount += pay.getAmount();
}
}
}
TradeData tradeData = Params.newTrade(true);
PayDetail payDetail = tradeData.getPayDetail();
String bitmap = BitmapConfig.BPC_SETTLEMENT;
payDetail.setTransType(TransactionsType.SETTLEMENT.name);
payDetail.setTransactionType(TransactionType.SETTLEMENT);
if(!flag) {
payDetail.setProcessCode(TransactionsType.SETTLEMENT.processCode);
} else {
bitmap = BitmapConfig.BPC_SETTLEMENT_TRAILER;
payDetail.setProcessCode("910000");
}
payDetail.setBatchNo(SystemParamsOperation.getInstance().getCurrentBatchNum());
SettleData settleData = new SettleData(
saleCount, saleAmount, preCount, preAmount, refundCount, refundAmount, caCount, caAmount);
payDetail.setSettleDataObj(settleData);
payDetail.setCardSettleData(CardSettleData.convertFromPayDetail(list));
long totalAmount = saleAmount + preAmount + refundAmount + caAmount;
String settlementData;
if (refundAmount != 0L) {
long creditTotal = saleAmount + preAmount + caAmount;
long subTotal = creditTotal - refundAmount;
if (subTotal < 0L) {
settlementData = "D" + String.format(Locale.getDefault(), "%012d", Math.abs(subTotal));
} else {
settlementData = "C" + String.format(Locale.getDefault(), "%012d", subTotal);
}
} else {
settlementData = "C" + String.format(Locale.getDefault(), "%012d", totalAmount);
}
if(totalAmount == 0 && (saleCount == 0 || preCount == 0 || refundCount == 0 || caCount == 0)) {
posSettlementReady = true;
posHasData = false;
checkAndLaunch();
checkIfBothSettlementsComplete();
return;
}
payDetail.setSettleData(settlementData);
payDetail.setAmount(totalAmount);
tradeData.setPayDetail(payDetail);
tradeData.setField60(SystemParamsOperation.getInstance().getCurrentBatchNum());
byte[] sendBytes = isoMsgX.buildISOPackets(tradeData, bitmap, MessageType.SETTLEMENT);
ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, new ISOCallback() {
@Override
public void onReceive(byte[] bytes, int length) {
Map<String, MsgField> responseMap = isoMsgX.parseISOPackets(bytes, length);
if (responseMap != null) {
String resultStr = "";
try {
resultStr = responseMap.get("F039").getDataStr();
} catch (NullPointerException e) {
e.printStackTrace();
payDetail.setIsNeedReversal(true);
return;
}
payDetail.setTradeAnswerCode(resultStr);
if (TextUtils.equals(resultStr, Constant.ANSWER_CODE_ACCEPT) || TextUtils.equals(resultStr, Constant.ANSWER_CODE_APPROVED)) {
payDetail.setIsNeedReversal(false);
} else if (TextUtils.equals(resultStr, "95") || TextUtils.equals(resultStr, "095")) {
payDetail.setIsNeedReversal(!flag);
}
}
}
@Override
public void onError(String msg) {
ISOSocket.getInstance().switchIp();
ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, this);
}
@Override
public void onComplete() {
LogUtil.d(TAG, "Auto settlement transaction completed successfully");
if(payDetail.getIsNeedReversal()) {
flag = true;
batchUploadProcess();
} else {
flag = false;
if (!list.isEmpty()) {
for (PayDetail p : list) {
repository.deletePayDetail(p);
}
}
repository.insertPayDetail(payDetail);
networkCutOver(payDetail);
}
}
});
}
};
repository.getSettlementPOS().observeForever(regularSettlementObserver);
}
private void batchUploadProcess() {
if(payDetails == null || payDetails.isEmpty()){
regularSettlementCompleted = false;
performSettlement();
return;
}
PayDetail payDetail = payDetails.get(i);
TradeData tradeData = new TradeData();
tradeData.setPayDetail(payDetail);
BatchUploadProcess.getInstance().enqueue(tradeData).startBatchUpload(new BatchListener() {
@Override
public void onSuccessBatch() {
if (i < payDetails.size() - 1) {
LogUtil.d(TAG, "Pay detail Size:" + payDetails.size());
LogUtil.d(TAG, "Count value:" + i);
i++;
batchUploadProcess();
} else {
regularSettlementCompleted = false;
performSettlement();
}
LogUtil.e(TAG, "Batch Upload Success");
}
@Override
public void onFailBatch() {
LogUtil.e(TAG, "Batch Upload Fail");
}
});
}
private void performQRSettlement() {
if (qrSettlementCompleted) {
return;
}
qrSettlementObserver = new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetailList) {
if (qrSettlementCompleted) {
return;
}
qrSettlementCompleted = true;
repository.getTransactionHistory().removeObserver(this);
if (payDetailList != null) {
int qrSaleCount = 0;
long qrSaleAmount = 0;
int qrRefundCount = 0;
long qrRefundAmount = 0;
ArrayList<PayDetail> qrTransactionsList = new ArrayList<>();
ArrayList<PayDetail> qrTransListAll = new ArrayList<>();
try {
for (PayDetail payDetail : payDetailList) {
if ((payDetail.getTransactionType() == TransactionsType.MMQR.value
|| payDetail.getTransactionType() == TransactionsType.MMQR_REFUND.value)
&& payDetail.getQrTransStatus() == 1) {
qrTransactionsList.add(payDetail);
if (payDetail.getTransactionType() == TransactionsType.MMQR.value) {
qrSaleCount++;
qrSaleAmount += payDetail.getAmount();
} else if (payDetail.getTransactionType() == TransactionsType.MMQR_REFUND.value) {
qrRefundCount++;
qrRefundAmount += payDetail.getAmount();
}
} else if (payDetail.getTransactionType() == TransactionsType.MMQR.value
|| payDetail.getTransactionType() == TransactionsType.MMQR_REFUND.value) {
qrTransListAll.add(payDetail);
}
}
} catch (IllegalStateException e) {
LogUtil.e(TAG, "QR Auto Settlement: Database cursor error - likely due to large data size: " + e);
qrSettlementReady = true;
qrHasData = false;
checkAndLaunch();
checkIfBothSettlementsComplete();
return;
}
if (!qrTransactionsList.isEmpty()) {
LogUtil.d(TAG, "Processing QR Auto Settlement with " + qrTransactionsList.size() + " transactions");
long totalAmount = qrSaleAmount - qrRefundAmount;
// Increment batch number for QR settlement
SystemParamsOperation.getInstance().getIncrementBatchNo();
// Create settlement data for QR transactions
SettleData settleData = new SettleData(
qrSaleCount, qrSaleAmount, 0, 0L, qrRefundCount, qrRefundAmount, 0, 0L);
TradeData tradeData = Params.newTrade(false);
PayDetail payDetail = tradeData.getPayDetail();
payDetail.setSettleDataObj(settleData);
payDetail.setTransactionType(TransactionsType.MMQR_SETTLEMENT.value);
payDetail.setTransType(TransactionsType.MMQR_SETTLEMENT.name);
payDetail.setAmount(totalAmount);
payDetail.setTradeAnswerCode("000");
payDetail.setBatchNo(SystemParamsOperation.getInstance().getCurrentBatchNum());
payDetail.setQrSettleData(QRSettleData.convertFromPayDetail(qrTransactionsList));
// Insert QR settlement record
repository.insertPayDetail(payDetail);
// Delete settled QR transactions
for (PayDetail pay : qrTransactionsList) {
repository.deletePayDetail(pay);
}
// Delete all other QR transactions (failed ones)
for (PayDetail pay : qrTransListAll) {
repository.deletePayDetail(pay);
}
// Push e-receipt data using repository directly
try {
EReceiptRequest request = EReceiptUtil.getInstance().generateQRSettlement(payDetail, QRSettleData.convertFromPayDetail(qrTransactionsList));
sendEReceipt(request);
LogUtil.d(TAG, "QR Settlement e-receipt data prepared and sent");
} catch (Exception e) {
LogUtil.e(TAG, "Error preparing QR settlement e-receipt: " + e.getMessage());
}
try {
// Create QR settlement data bundle
Bundle qrData = new Bundle();
qrData.putInt("QR_SALE_COUNT", qrSaleCount);
qrData.putLong("QR_SALE_AMOUNT", qrSaleAmount);
qrData.putInt("QR_REFUND_COUNT", qrRefundCount);
qrData.putLong("QR_REFUND_AMOUNT", qrRefundAmount);
qrData.putLong("QR_TOTAL_AMOUNT", totalAmount);
qrData.putSerializable("QR_TRANS_LIST", qrTransactionsList);
// Use notification-based approach to reliably launch the app
pendingQrData = qrData;
qrHasData = true;
// launchAppWithSettlementResult("AUTO_QR_SETTLEMENT", null, qrData,false);
} catch (Exception e) {
LogUtil.e(TAG, "Error launching app for QR settlement: " + e.getMessage());
qrHasData = false;
e.printStackTrace();
}
LogUtil.d(TAG, "QR Auto Settlement completed successfully");
} else {
LogUtil.d(TAG, "No QR transactions found for auto settlement");
qrHasData = false;
}
}
qrSettlementReady = true;
checkAndLaunch();
checkIfBothSettlementsComplete();
}
};
repository.getTransactionHistory().observeForever(qrSettlementObserver);
}
private void checkIfBothSettlementsComplete() {
if (regularSettlementCompleted && qrSettlementCompleted) {
LogUtil.d(TAG, "Both settlements completed, stopping service");
stopSelf();
}
}
@Override
public void onCreate() {
super.onCreate();
handler = new Handler();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
// Clean up observers to prevent memory leaks
if (regularSettlementObserver != null && repository != null) {
repository.getSettlementPOS().removeObserver(regularSettlementObserver);
}
if (qrSettlementObserver != null && repository != null) {
repository.getTransactionHistory().removeObserver(qrSettlementObserver);
}
}
private void networkCutOver(PayDetail payDetail) {
EchoTestProcess.getInstance().enqueueCutOver().startSignOn(new SignOnListener() {
@Override
public void onSuccessSignOn() {
pendingPayDetail = payDetail;
posHasData = true;
posSettlementReady = true;
checkAndLaunch();
checkIfBothSettlementsComplete();
}
@Override
public void onFailureSignOn(Integer resultCode) {
LogUtil.d(TAG,"Failure Cut Over message: "+resultCode);
// POS done but cutover failed — still mark ready
posSettlementReady = true;
posHasData = false;
checkAndLaunch();
checkIfBothSettlementsComplete();
}
@Override
public void onNetworkFailSignOn(String message) {
LogUtil.d(TAG,"Network Fail Cut Over message: "+message);
posSettlementReady = true;
posHasData = false;
checkAndLaunch();
checkIfBothSettlementsComplete();
}
});
}
private void checkAndLaunch() {
// !!!! need to add flag, if don't want both at once
if (!posSettlementReady || !qrSettlementReady) {
LogUtil.d(TAG, "Waiting for both settlements... POS=" + posSettlementReady + " QR=" + qrSettlementReady);
return;
}
LogUtil.d(TAG, "Both settlements ready. POS has data=" + posHasData + ", QR has data=" + qrHasData);
if (!posHasData && !qrHasData) {
LogUtil.d(TAG, "No settlement data to display");
return;
}
launchMainActivity();
}
private void launchMainActivity() {
private void launchSettlementFragment() {
try {
Intent launchIntent = new Intent(this, MainActivity.class);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
if (posHasData && pendingPayDetail != null) {
launchIntent.putExtra("AUTO_SETTLEMENT", true);
launchIntent.putExtra("EXTRA_TRANSACTION_TYPE", TransactionsType.SETTLEMENT.value);
launchIntent.putExtra("EXTRA_PAY_DETAIL", pendingPayDetail);
}
if (qrHasData && pendingQrData != null) {
launchIntent.putExtra("AUTO_QR_SETTLEMENT", true);
launchIntent.putExtra("EXTRA_QR_TRANSACTION_TYPE", TransactionsType.MMQR_SETTLEMENT.value);
launchIntent.putExtras(pendingQrData);
}
launchIntent.putExtra("AUTO_SETTLEMENT", true);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
@ -638,57 +101,35 @@ public class AutoSettleService extends Service {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "SETTLEMENT_COMPLETE")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Auto Settlement Complete")
.setContentText("Tap to view settlement results and print receipt")
.setContentTitle("Auto Settlement")
.setContentText("Tap to view settlement and print receipt")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setFullScreenIntent(pendingIntent, true);
notificationManager.notify(2001, builder.build());
// Reset state
pendingPayDetail = null;
pendingQrData = null;
posSettlementReady = false;
qrSettlementReady = false;
posHasData = false;
qrHasData = false;
startActivity(launchIntent);
} catch (Exception e) {
LogUtil.e(TAG, "Error launching MainActivity: " + e.getMessage());
LogUtil.e(TAG, "Error launching settlement: " + e.getMessage());
e.printStackTrace();
}
}
private void sendEReceipt(EReceiptRequest request) {
try {
LogUtil.d(TAG, "Sending e-receipt: " + new Gson().toJson(request));
repository.sendReceipt(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> LogUtil.d(TAG, "E-Receipt sent successfully: " + response.getMessage()),
error -> {
if (error instanceof HttpException) {
HttpException httpEx = (HttpException) error;
try {
String errorJson = httpEx.response().errorBody().string();
LogUtil.e(TAG, "E-Receipt error: " + errorJson);
} catch (Exception e) {
LogUtil.e(TAG, "E-Receipt send failed: " + error.getMessage());
}
} else {
LogUtil.e(TAG, "E-Receipt send failed: " + error.getMessage());
}
}
);
} catch (Exception e) {
LogUtil.e(TAG, "Error sending e-receipt: " + e.getMessage());
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}

View File

@ -132,6 +132,8 @@ public class SharedViewModel extends ViewModel {
public MutableLiveData<Boolean> _manualEntryStatus = new MutableLiveData<>();
public MutableLiveData<Boolean> autoSettle = new MutableLiveData<>(false);
public MutableLiveData<Boolean> fullVoidPreauthStatus = new MutableLiveData<>();
public MutableLiveData<Boolean> partialVoidPreauthStatus = new MutableLiveData<>();

View File

@ -63,7 +63,7 @@ public class QRPayFragment extends DataBindingFragment {
features.add(new QRPayItem("Refund", R.drawable.ic_refund, true));
}
features.add(new QRPayItem("History", R.drawable.ic_history, true));
// features.add(new QRPayItem("Mock QR", R.drawable.ic_qr_pay, true));
features.add(new QRPayItem("Mock QR", R.drawable.ic_qr_pay, true));
// List<QRPayItem> features = List.of(
// new QRPayItem("Sale", R.drawable.ic_qr_pay, true),

View File

@ -529,6 +529,17 @@ public class SettlementTransactionFragment extends DataBindingFragment implement
safeNavigateToRouteId();
}
private void performAutoSettle() {
if (isUSDSettleNeeded()) {
currencySelectionHelper.applyCardCurrencyAndHost("USD");
settlementViewModel.startSettlementProcess(usdBucket, false);
} else {
currencySelectionHelper.applyCardCurrencyAndHost("MMK");
settlementViewModel.startSettlementProcess(mmkBucket, true);
}
showLoadingDialog("Sending ...");
}
private boolean isUSDSettleNeeded() {
return (usdBucket.saleCount != 0 || usdBucket.preCount != 0 || usdBucket.refundCount != 0 || usdBucket.caCount != 0);
}
@ -565,6 +576,13 @@ public class SettlementTransactionFragment extends DataBindingFragment implement
settlementViewModel.settlementType.setValue(sharedViewModel.settlementType.getValue());
settlementViewModel.isNoData.observe(getViewLifecycleOwner(), noData -> {
if (Boolean.TRUE.equals(sharedViewModel.autoSettle.getValue()) && noData == false) {
sharedViewModel.autoSettle.setValue(false);
delayFunctionCall(this::performAutoSettle);
}
});
settlementViewModel.resultString.observe(getViewLifecycleOwner(), transactionStatus -> {
switch (transactionStatus) {
case ON_BATCH_UPLOAD:
@ -605,6 +623,10 @@ public class SettlementTransactionFragment extends DataBindingFragment implement
payDetail.setCardSettleData(CardSettleData.convertFromPayDetail(cardTransactionsList));
}
if (qrTransactionsList != null && !qrTransactionsList.isEmpty()) {
payDetail.setQrSettleData(QRSettleData.convertFromPayDetail(qrTransactionsList));
}
sharedViewModel.payDetail.setValue(payDetail);
EReceiptRequest request = EReceiptUtil.getInstance().generateMPUReceipt(sharedViewModel.payDetail.getValue());

View File

@ -3,12 +3,15 @@ package com.utsmm.kbz.util;
import com.utsmm.kbz.BuildConfig;
import com.utsmm.kbz.util.enums.TransResultStatus;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.network.model.e_receipt.CardTransaction;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptRequest;
import com.utsmyanmar.baselib.network.model.e_receipt.Transaction;
import com.utsmyanmar.paylibs.model.CardSettleData;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.QRSettleData;
import com.utsmyanmar.paylibs.model.SettleData;
import com.utsmyanmar.paylibs.system.BaseErrorCode;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.POSUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
@ -174,10 +177,12 @@ public class EReceiptUtil {
String currentTimeStamp = new java.text.SimpleDateFormat("MMddHHmmss", java.util.Locale.getDefault())
.format(new java.util.Date());
EReceiptRequest request = new EReceiptRequest();
String hostId = SystemParamsOperation.getInstance().getHostId();
request.setDE3(convertTransactionType(payDetail.getTransactionType()));
request.setDE7(currentTimeStamp);
request.setDE11(payDetail.getVoucherNo());
request.setHostId(hostId);
if (payDetail.getTransactionType() == TransactionsType.SETTLEMENT.value) {
SettleData settleData = payDetail.getSettleDataObj();
@ -186,7 +191,6 @@ public class EReceiptUtil {
request.setDE4(getDecimalAmount(totalAmt));
request.setDE63_01(settleData.getSaleCount() + "");
request.setDE63_02(getDecimalAmount(settleData.getSaleAmount()));
request.setDE63_03(settleData.getRefundCount() + "");
@ -203,6 +207,37 @@ public class EReceiptUtil {
request.setDE37("0000");
request.setDE49(currencyCodeToText(payDetail.getCurrencyCode()));
request.setDE14("");
request.setDE24(payDetail.getVoucherNo());
request.setDE43(SystemParamsOperation.getInstance().getMerchantName());
request.setDE3("CST");
List<CardSettleData> cardSettleDataList = payDetail.getCardSettleData();
if (cardSettleDataList != null && !cardSettleDataList.isEmpty()) {
request.setCardTransactions(buildCardTransactions(cardSettleDataList));
}
List<QRSettleData> qrSettleDataList = payDetail.getQrSettleData();
if(qrSettleDataList != null && !qrSettleDataList.isEmpty()){
request.setTransactions(buildTransactions(qrSettleDataList));
long qrRefundTotal = 0;
long qrSaleTotal = 0;
for (QRSettleData data : qrSettleDataList) {
if (data.getTransactionType() == TransactionsType.MMQR_REFUND.value) {
qrRefundTotal += data.getAmount();
} else if (data.getTransactionType() == TransactionsType.MMQR.value) {
qrSaleTotal += data.getAmount();
}
}
request.setDE63_09(getDecimalAmount(qrRefundTotal));
request.setDE63_10(getDecimalAmount(qrSaleTotal));
}else{
LogUtil.d("KMK", "QR settle data is null");
request.setDE63_09(getDecimalAmount(0));
request.setDE63_10(getDecimalAmount(0));
}
} else {
request.setDE2(POSUtil.getInstance().getCardNumMasking(payDetail.getCardNo()));
@ -305,6 +340,29 @@ public class EReceiptUtil {
return currencyCode;
}
private static List<CardTransaction> buildCardTransactions(List<CardSettleData> cardSettleDataList) {
List<CardTransaction> list = new ArrayList<>();
for (CardSettleData card : cardSettleDataList) {
CardTransaction ct = new CardTransaction();
ct.setCardType(card.getCardType());
ct.setCardNumber(POSUtil.getInstance().getCardNumMasking(card.getCardNum()));
ct.setInvoiceNumber(card.getInvoiceNum());
ct.setTransactionType(card.getTransType());
ct.setAmount(getDecimalAmountStatic(card.getAmount()));
ct.setApproveCode(card.getApprovalCode());
String dateTime = card.getTransDate() + "T" + card.getTransTime() + "+08:00";
ct.setTransactionTime(dateTime);
list.add(ct);
}
return list;
}
private static String getDecimalAmountStatic(long amount) {
double realAmount = amount / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
return df.format(realAmount);
}
private static List<Transaction> buildTransactions(List<QRSettleData> qrSettleData) {
List<Transaction> list = new ArrayList<>();
@ -324,7 +382,7 @@ public class EReceiptUtil {
);
txn.setDate(pay.getDate()); // yyyyMMdd
txn.setTime(pay.getTime()); // HHmmss
txn.setAmount(amount);
txn.setAmount(pay.getTransactionType() == TransactionsType.MMQR_REFUND.value ? "-" + amount : amount);
list.add(txn);
}
@ -355,6 +413,4 @@ public class EReceiptUtil {
}
return "E";
}
}
}

View File

@ -223,6 +223,7 @@ public class TMSSetupsImpl implements TMSSetups{
// PRIMARY HOST (Card #1)
if (!cardHosts.isEmpty()) {
SystemParamsOperation.getInstance().setHostId(cardHosts.get(0).getId());
SiriusHost primaryCard = cardHosts.get(0);
SystemParamsOperation.getInstance().setHostName(primaryCard.getName() == null ? "" : primaryCard.getName());
SystemParamsOperation.getInstance().setTerminalId(primaryCard.getTid() == null ? "" : primaryCard.getTid());
@ -233,7 +234,6 @@ public class TMSSetupsImpl implements TMSSetups{
String secondaryIp = primaryCard.getSecondaryIP();
SystemParamsOperation.getInstance().setSecIpAddress(secondaryIp != null && secondaryIp.contains(":") ? secondaryIp.trim() : "");
if (!TextUtils.isEmpty(primaryCard.getCurrency())) {
SystemParamsOperation.getInstance().setCurrencyType(currencyTextToCurrencyType(primaryCard.getCurrency()));
}
@ -460,7 +460,7 @@ public class TMSSetupsImpl implements TMSSetups{
SystemParamsOperation.getInstance().setRandomPinPad(parseBoolean(data));
} else if (TextUtils.equals(name,"clear_batch_time")) {
SystemParamsOperation.getInstance().setClearBatchTime(data);
SystemParamsOperation.getInstance().setClearBatchTime("22:00");
} else if (TextUtils.equals(name,"alert_sound_enable")) {
SystemParamsOperation.getInstance().setAlertSound(parseBoolean(data));

View File

@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 41,
"versionName": "2.36-uat",
"versionCode": 44,
"versionName": "2.39-uat",
"outputFile": "app-uat-release.apk"
}
],

View File

@ -0,0 +1,70 @@
package com.utsmyanmar.baselib.network.model.e_receipt;
public class CardTransaction {
private String cardType;
private String cardNumber;
private String invoiceNumber;
private String transactionType;
private String amount;
private String approveCode;
private String transactionTime;
public CardTransaction() {}
public String getCardType() {
return cardType;
}
public void setCardType(String cardType) {
this.cardType = cardType;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getInvoiceNumber() {
return invoiceNumber;
}
public void setInvoiceNumber(String invoiceNumber) {
this.invoiceNumber = invoiceNumber;
}
public String getTransactionType() {
return transactionType;
}
public void setTransactionType(String transactionType) {
this.transactionType = transactionType;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
public String getApproveCode() {
return approveCode;
}
public void setApproveCode(String approveCode) {
this.approveCode = approveCode;
}
public String getTransactionTime() {
return transactionTime;
}
public void setTransactionTime(String transactionTime) {
this.transactionTime = transactionTime;
}
}

View File

@ -27,6 +27,8 @@ public class EReceiptRequest {
private String DE63_06;
private String DE63_07;
private String DE63_08;
private String DE63_09;
private String DE63_10;
private String serial;
private String appId;
@ -43,7 +45,12 @@ public class EReceiptRequest {
private String hostId;
private String paymentIdentifier;
private String DE24;
private String DE43;
private String DE14;
private List<Transaction> transactions;
private List<CardTransaction> cardTransactions;
public EReceiptRequest() {}
@ -269,6 +276,34 @@ public class EReceiptRequest {
this.cardLabel = cardLabel;
}
public void setMmqrRef(String mmqrRef){ this.mmqrRef = mmqrRef; }
public String getDE24() { return DE24; }
public void setDE24(String DE24) { this.DE24 = DE24; }
public String getDE43() { return DE43; }
public void setDE43(String DE43) { this.DE43 = DE43; }
public String getDE14() { return DE14; }
public void setDE14(String DE14) { this.DE14 = DE14; }
public List<CardTransaction> getCardTransactions() { return cardTransactions; }
public void setCardTransactions(List<CardTransaction> cardTransactions) { this.cardTransactions = cardTransactions; }
public String getDE63_09() {
return DE63_09;
}
public void setDE63_09(String DE63_09) {
this.DE63_09 = DE63_09;
}
public String getDE63_10() {
return DE63_10;
}
public void setDE63_10(String DE63_10) {
this.DE63_10 = DE63_10;
}
}

View File

@ -31,3 +31,6 @@ android.nonTransitiveRClass=true
android.enableJetifier=false
ERECEIPT_SECRET=y812J21lhha11OS
#ERECEIPT_SECRET=8f4df38d1001bcc4620b5c736c66a03eef4653eb3ba31105faa2f2ee294c4a46
# Enabled parallel sync for Gradle 9.4+
org.gradle.tooling.parallel=true

11
opencode.json Normal file
View File

@ -0,0 +1,11 @@
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"codebase-memory": {
"type": "local",
"command": "codebase-memory-mcp",
"args": [],
"enabled": true
}
}
}

View File

@ -218,14 +218,24 @@ public abstract class BaseXPrint {
printTransDetailSettlement(payDetail, cardSettleDataList);
return;
}
SystemParamsOperation sp = SystemParamsOperation.getInstance();
SettleData settleData = payDetail.getSettleDataObj();
print3ColumnsString("TRX TYPE ", "CNT", "AMOUNT");
print3ColumnsString("SALE ", countStringFormat(settleData.getSaleCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getSaleAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
print3ColumnsString("VOID SALE ", countStringFormat(settleData.getVoidSaleCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getVoidSaleAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
print3ColumnsString("PREAUTH COMP ", countStringFormat(settleData.getPreAuthCompCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getPreAuthCompAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
print3ColumnsString("CASH ADV ", countStringFormat(settleData.getCashAdvanceCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getCashAdvanceAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
print3ColumnsString("REFUND ", countStringFormat(settleData.getRefundCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getRefundAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
if(sp.getVoidStatus()){
print3ColumnsString("VOID SALE ", countStringFormat(settleData.getVoidSaleCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getVoidSaleAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
}
if(sp.getPreAuthStatus()){
print3ColumnsString("PREAUTH COMP ", countStringFormat(settleData.getPreAuthCompCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getPreAuthCompAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
}
if(sp.getCashAdvanceStatus()){
print3ColumnsString("CASH ADV ", countStringFormat(settleData.getCashAdvanceCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getCashAdvanceAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
}
if(sp.getRefundStatus()){
print3ColumnsString("REFUND ", countStringFormat(settleData.getRefundCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getRefundAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
}
dotBreak();
long totalAmount = settleData.getSaleAmount() + settleData.getPreAuthCompAmount() + settleData.getCashAdvanceAmount()
- settleData.getVoidSaleAmount() - settleData.getRefundAmount();
@ -254,23 +264,93 @@ public abstract class BaseXPrint {
}
dotBreak();
SettleData settleData = payDetail.getSettleDataObj();
long totalAmount = settleData.getSaleAmount() + settleData.getPreAuthCompAmount() + settleData.getCashAdvanceAmount()
- settleData.getVoidSaleAmount() - settleData.getRefundAmount();
boolean isDuelCurrency = SystemParamsOperation.getInstance().getDuelCurrencyStatus();
printer.appendPrnStr("GRAND TOTAL", fontLarge, AlignEnum.LEFT, true);
emptyLine(1);
print3ColumnsString("TRX TYPE ", "CNT", "AMOUNT");
print3ColumnsString("SALE ", countStringFormat(settleData.getSaleCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getSaleAmount(), isDecimalEnabled));
print3ColumnsString("VOID SALE ", countStringFormat(settleData.getVoidSaleCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getVoidSaleAmount(), isDecimalEnabled));
print3ColumnsString("PREAUTH COMP ", countStringFormat(settleData.getPreAuthCompCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getPreAuthCompAmount(), isDecimalEnabled));
print3ColumnsString("CASH ADV ", countStringFormat(settleData.getCashAdvanceCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getCashAdvanceAmount(), isDecimalEnabled));
print3ColumnsString("REFUND ", countStringFormat(settleData.getRefundCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getRefundAmount(), isDecimalEnabled));
dotBreak();
print2ColumnsString("TOTAL", PrintUtils.getInstance().getSeparatorNumberFormat(totalAmount, isDecimalEnabled));
if (isDuelCurrency) {
Map<String, CurrencySettlementSummary> currencyMap = buildCurrencySummaries(cardSettleDataList);
if (currencyMap.size() > 1) {
for (Map.Entry<String, CurrencySettlementSummary> entry : currencyMap.entrySet()) {
printSettlementCurrencySection(entry.getKey(), entry.getValue());
}
dotBreak();
} else {
printSingleSettleSummary(cardSettleDataList, isDecimalEnabled);
}
} else {
printSingleSettleSummary(cardSettleDataList, isDecimalEnabled);
}
emptyLine(1);
}
private void printSingleSettleSummary(List<CardSettleData> cardSettleDataList, boolean isDecimalEnabled) {
Map<String, CurrencySettlementSummary> currencyMap = buildCurrencySummaries(cardSettleDataList);
SystemParamsOperation sp = SystemParamsOperation.getInstance();
if (currencyMap.size() == 1) {
Map.Entry<String, CurrencySettlementSummary> entry = currencyMap.entrySet().iterator().next();
printSettlementCurrencySection(entry.getKey(), entry.getValue());
} else {
SettleData settleData = new SettleData();
for (CardSettleData csd : cardSettleDataList) {
applyCardSettlementSummary(settleData, csd);
}
print3ColumnsString("TRX TYPE ", "CNT", "AMOUNT");
print3ColumnsString("SALE ", countStringFormat(settleData.getSaleCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getSaleAmount(), isDecimalEnabled));
if(sp.getVoidStatus()){
print3ColumnsString("VOID SALE ", countStringFormat(settleData.getVoidSaleCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getVoidSaleAmount(), isDecimalEnabled));
}
if(sp.getPreAuthStatus()){
print3ColumnsString("PREAUTH COMP ", countStringFormat(settleData.getPreAuthCompCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getPreAuthCompAmount(), isDecimalEnabled));
}
if(sp.getCashAdvanceStatus()){
print3ColumnsString("CASH ADV ", countStringFormat(settleData.getCashAdvanceCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getCashAdvanceAmount(), isDecimalEnabled));
}
if(sp.getRefundStatus()){
print3ColumnsString("REFUND ", countStringFormat(settleData.getRefundCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getRefundAmount(), isDecimalEnabled));
}
dotBreak();
long totalAmount = settleData.getSaleAmount() + settleData.getPreAuthCompAmount() + settleData.getCashAdvanceAmount()
- settleData.getVoidSaleAmount() - settleData.getRefundAmount();
print2ColumnsString("TOTAL", PrintUtils.getInstance().getSeparatorNumberFormat(totalAmount, isDecimalEnabled));
}
}
private Map<String, CurrencySettlementSummary> buildCurrencySummaries(List<CardSettleData> cardSettleDataList) {
Map<String, CurrencySettlementSummary> currencyMap = new LinkedHashMap<>();
for (CardSettleData csd : cardSettleDataList) {
String currency = normalizeSettlementCurrency(csd.getCurrencyCode());
CurrencySettlementSummary summary = currencyMap.get(currency);
if (summary == null) {
summary = new CurrencySettlementSummary();
currencyMap.put(currency, summary);
}
applyCardSettlementSummary(summary, csd);
}
return currencyMap;
}
private void applyCardSettlementSummary(SettleData settleData, CardSettleData cardSettleData) {
String transType = cardSettleData.getTransType() == null ? "" : cardSettleData.getTransType().trim().toUpperCase(Locale.getDefault());
long amount = cardSettleData.getAmount();
if ("SALE".equals(transType)) {
settleData.setSaleCount(settleData.getSaleCount() + 1);
settleData.setSaleAmount(settleData.getSaleAmount() + amount);
} else if ("PREAUTH COMPLETION".equals(transType) || "PRE_AUTH_COMPLETE".equals(transType)) {
settleData.setPreAuthCompCount(settleData.getPreAuthCompCount() + 1);
settleData.setPreAuthCompAmount(settleData.getPreAuthCompAmount() + amount);
} else if ("REFUND".equals(transType)) {
settleData.setRefundCount(settleData.getRefundCount() + 1);
settleData.setRefundAmount(settleData.getRefundAmount() + amount);
} else if ("CASH_OUT".equals(transType) || "CASH_ADVANCE".equals(transType)) {
settleData.setCashAdvanceCount(settleData.getCashAdvanceCount() + 1);
settleData.setCashAdvanceAmount(settleData.getCashAdvanceAmount() + amount);
}
}
protected void printMMQRReport(List<QRSettleData> qrList) {
if (qrList == null || qrList.isEmpty()) return;
@ -319,18 +399,25 @@ public abstract class BaseXPrint {
// return;
// }
boolean isDecimalEnabled = SystemParamsOperation.getInstance().getDecimalEnable();
SystemParamsOperation sp = SystemParamsOperation.getInstance();
int totalCount = summary.saleCount + summary.preAuthCompCount + summary.refundCount + summary.cashAdvanceCount;
long totalAmount = summary.saleAmount + summary.preAuthCompAmount + summary.cashAdvanceAmount - summary.refundAmount;
emptyLine(1);
printString("CURRENCY : " + currencyLabel, true);
print3ColumnsString("TRX TYPE ", "CNT", "AMOUNT");
print3ColumnsString("SALE ", countStringFormat(summary.saleCount), PrintUtils.getInstance().getSeparatorNumberFormat(summary.saleAmount, isDecimalEnabled));
print3ColumnsString("PREAUTH COMP ", countStringFormat(summary.preAuthCompCount), PrintUtils.getInstance().getSeparatorNumberFormat(summary.preAuthCompAmount, isDecimalEnabled));
if (summary.cashAdvanceCount != 0) {
print3ColumnsString("CASH ADVANCE ", countStringFormat(summary.cashAdvanceCount), PrintUtils.getInstance().getSeparatorNumberFormat(summary.cashAdvanceAmount, isDecimalEnabled));
if(sp.getPreAuthStatus()){
print3ColumnsString("PREAUTH COMP ", countStringFormat(summary.preAuthCompCount), PrintUtils.getInstance().getSeparatorNumberFormat(summary.preAuthCompAmount, isDecimalEnabled));
}
if (summary.cashAdvanceCount != 0) {
if(sp.getCashAdvanceStatus()){
print3ColumnsString("CASH ADV ", countStringFormat(summary.cashAdvanceCount), PrintUtils.getInstance().getSeparatorNumberFormat(summary.cashAdvanceAmount, isDecimalEnabled));
}
}
if(sp.getRefundStatus()){
print3ColumnsString("REFUND ", countStringFormat(summary.refundCount), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(summary.refundAmount, isDecimalEnabled));
}
print3ColumnsString("REFUND ", countStringFormat(summary.refundCount), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(summary.refundAmount, isDecimalEnabled));
print3ColumnsString("TOTAL(" + currencyLabel + ") ", countStringFormat(totalCount), PrintUtils.getInstance().getSeparatorNumberFormat(totalAmount, isDecimalEnabled));
}
@ -969,6 +1056,8 @@ public abstract class BaseXPrint {
return;
}
SystemParamsOperation sp = SystemParamsOperation.getInstance();
Map<String, TempSummary> currencyMap = new HashMap<>();
for (PayDetail item : list) {
@ -1029,47 +1118,49 @@ public abstract class BaseXPrint {
fontNormal, false);
}
if (data.voidSaleCount > 0) {
if (data.voidSaleCount > 0 && sp.getVoidStatus()) {
print3ColumnsStringWithAlign("(" + data.voidSaleCount + ")",
"VOID",
"- " + PrintUtils.getInstance().getSeparatorNumberFormat(data.voidSaleAmount, isDecimalEnabled),
fontNormal, false);
}
if (data.cashOutCount > 0) {
if (data.cashOutCount > 0 && sp.getCashAdvanceStatus()) {
print3ColumnsStringWithAlign("(" + data.cashOutCount + ")",
"CASH ADV",
PrintUtils.getInstance().getSeparatorNumberFormat(data.cashOutAmount, isDecimalEnabled),
fontNormal, false);
}
if (data.preAuthSaleCount > 0) {
print3ColumnsStringWithAlign("(" + data.preAuthSaleCount + ")",
"PRE-AUTH",
PrintUtils.getInstance().getSeparatorNumberFormat(data.preAuthSaleAmount, isDecimalEnabled),
fontNormal, false);
}
// if (data.preAuthSaleCount > 0) {
// print3ColumnsStringWithAlign("(" + data.preAuthSaleCount + ")",
// "PRE-AUTH",
// PrintUtils.getInstance().getSeparatorNumberFormat(data.preAuthSaleAmount, isDecimalEnabled),
// fontNormal, false);
// }
//
// if (data.preAuthVoidCount > 0) {
// print3ColumnsStringWithAlign("(" + data.preAuthVoidCount + ")",
// "PREAUTH VOID",
// "- " + PrintUtils.getInstance().getSeparatorNumberFormat(data.preAuthVoidAmount, isDecimalEnabled),
// fontNormal, false);
// }
if (data.preAuthVoidCount > 0) {
print3ColumnsStringWithAlign("(" + data.preAuthVoidCount + ")",
"PREAUTH VOID",
"- " + PrintUtils.getInstance().getSeparatorNumberFormat(data.preAuthVoidAmount, isDecimalEnabled),
fontNormal, false);
}
if (data.refundCount > 0) {
print3ColumnsStringWithAlign("(" + data.refundCount + ")",
"REFUND",
"- " + PrintUtils.getInstance().getSeparatorNumberFormat(data.refundAmount, isDecimalEnabled),
fontNormal, false);
}
if (data.preAuthCompCount > 0) {
if (data.preAuthCompCount > 0 && sp.getPreAuthStatus()) {
print3ColumnsStringWithAlign("(" + data.preAuthCompCount + ")",
"PREAUTH COMP",
PrintUtils.getInstance().getSeparatorNumberFormat(data.preAuthCompAmount, isDecimalEnabled),
fontNormal, false);
}
if (data.refundCount > 0 && sp.getRefundStatus()) {
print3ColumnsStringWithAlign("(" + data.refundCount + ")",
"REFUND",
"- " + PrintUtils.getInstance().getSeparatorNumberFormat(data.refundAmount, isDecimalEnabled),
fontNormal, false);
}
}
dotBreak();

View File

@ -1855,6 +1855,17 @@ public class SystemParamsOperation {
return paramsSettings.getSecHostId();
}
public String getHostId(){
SystemParamsSettings paramsSettings = getSystemParamsSettings();
return paramsSettings.getHostId();
}
public void setHostId(String hostId){
SystemParamsSettings paramsSettings = getSystemParamsSettings();
paramsSettings.setHostId(hostId);
saveSystemParamsSettings(paramsSettings);
}
public String getMerchantAddress3() {
SystemParamsSettings paramsSettings = getSystemParamsSettings();
return paramsSettings.getMerchantAddress3();

View File

@ -104,6 +104,7 @@ public class SystemParamsSettings implements Serializable {
private String hostName = "";
private String secHostId = "";
private String hostId = "";
private String secHostName = "";
private String thirdHostName = "";
@ -1205,6 +1206,14 @@ public class SystemParamsSettings implements Serializable {
this.jcbTerminalCapability = jcbTerminalCapability;
}
public String getHostId() {
return hostId;
}
public void setHostId(String hostId) {
this.hostId = hostId;
}
/* // 流水号起始
private String serialNum = Configs.getInstance().SERIAL_NUM();
// 批次号起始