This commit is contained in:
moon 2026-06-29 11:41:56 +06:30
parent 22d6066933
commit b87f987466
15 changed files with 98 additions and 67 deletions

32
AGENT.MD Normal file
View File

@ -0,0 +1,32 @@
# Ponytail, lazy senior dev mode
You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.
Before writing any code, stop at the first rung that holds:
1. Does this need to be built at all? (YAGNI)
2. Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it.
3. Does the standard library already do this? Use it.
4. Does a native platform feature cover it? Use it.
5. Does an already-installed dependency solve it? Use it.
6. Can this be one line? Make it one line.
7. Only then: write the minimum code that works.
The ladder runs after you understand the problem, not instead of it: read the task and the code it touches, trace the real flow end to end, then climb.
Bug fix = root cause, not symptom: a report names a symptom. Grep every caller of the function you touch and fix the shared function once — one guard there is a smaller diff than one per caller, and patching only the path the ticket names leaves a sibling caller still broken.
Rules:
- No abstractions that weren't explicitly requested.
- No new dependency if it can be avoided.
- No boilerplate nobody asked for.
- Deletion over addition. Boring over clever. Fewest files possible.
- Shortest working diff wins, but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug.
- Question complex requests: "Do you actually need X, or does Y cover it?"
- Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm.
- Mark intentional simplifications with a `ponytail:` comment. If the shortcut has a known ceiling (global lock, O(n²) scan, naive heuristic), the comment names the ceiling and the upgrade path.
Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.
(Yes, this file also applies to agents working on the ponytail repo itself. Especially to them.)

View File

@ -15,4 +15,22 @@
- Control Bar disabled in normal status and could not be down to go settings - Control Bar disabled in normal status and could not be down to go settings
- But can auto enable when password is correct in function tab - But can auto enable when password is correct in function tab
- **Void Fixed** - **Void Fixed**
- Void transaction fixed - Void transaction fixed
## [2.34] - 29-05-2026
- **General**
- Settlement fix for qr records missing after Card settlement run
- fixed receipt printing loop for auto settlement
-
- **MMQR Refund**
- new auto-retry when MMQR status is "REFUNDING" in response
- no more fail records on refund list if "DUPLICATED" response
- removed "CANCEL" records in MMQR History
-
- **CARD Payments**
- signature saved in pos device
- signature re-show on re-printing receipt
- 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

View File

@ -14,8 +14,8 @@ android {
applicationId "com.utsmm.kbz" applicationId "com.utsmm.kbz"
minSdk 24 minSdk 24
targetSdk 33 targetSdk 33
versionCode 36 versionCode 39
versionName "2.31" versionName "2.34"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

View File

@ -158,10 +158,10 @@ public class MainActivity extends AppCompatActivity implements
private void initKey(){ private void initKey(){
// SystemParamsOperation.getInstance().setTmsAddress("https://api-tms.kbzbank.com/sirius"); // SystemParamsOperation.getInstance().setTmsAddress("https://api-tms.kbzbank.com/sirius");
// SystemParamsOperation.getInstance().setEReceiptAddress("https://api-tms.kbzbank.com/receipt"); // SystemParamsOperation.getInstance().setEReceiptAddress("https://api-tms.kbzbank.com/receipt");
// SystemParamsOperation.getInstance().setTmsAddress("https://api-tms-uat.kbzbank.com:8443/sirius"); SystemParamsOperation.getInstance().setTmsAddress("https://api-tms-uat.kbzbank.com:8443/sirius");
// SystemParamsOperation.getInstance().setEReceiptAddress("https://api-tms-uat.kbzbank.com:8443/receipt"); SystemParamsOperation.getInstance().setEReceiptAddress("https://api-tms-uat.kbzbank.com:8443/receipt");
SystemParamsOperation.getInstance().setTmsAddress("https://sirius-nest.utsmyanmar.com/api/v1"); // SystemParamsOperation.getInstance().setTmsAddress("https://sirius-nest.utsmyanmar.com/api/v1");
SystemParamsOperation.getInstance().setEReceiptAddress("https://receipt-nest.utsmyanmar.com"); // SystemParamsOperation.getInstance().setEReceiptAddress("https://receipt-nest.utsmyanmar.com");
PinPad pinPad = MyApplication.getInstance().deviceEngine.getPinPad(); PinPad pinPad = MyApplication.getInstance().deviceEngine.getPinPad();
byte[] encryptedPIK = SystemParamsOperation.getInstance().getPIK(); byte[] encryptedPIK = SystemParamsOperation.getInstance().getPIK();

View File

@ -345,12 +345,14 @@ public class ProcessingFragment extends DataBindingFragment {
handleAfterTransaction(); handleAfterTransaction();
break; break;
case SUCCESS: case SUCCESS:
if(requiresSignature()){ sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, true, BaseErrorCode.getCode(payDetail.getTradeAnswerCode())));
handleAfterTransaction(); handleAfterTransaction();
}else{ // if(requiresSignature()){
sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, true, BaseErrorCode.getCode(payDetail.getTradeAnswerCode()))); // handleAfterTransaction();
handleAfterTransaction(); // }else{
} // sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, true, BaseErrorCode.getCode(payDetail.getTradeAnswerCode())));
// handleAfterTransaction();
// }
break; break;
case OFFLINE_SUCCESS: case OFFLINE_SUCCESS:
if(SystemParamsOperation.getInstance().getDemoStatus()) { if(SystemParamsOperation.getInstance().getDemoStatus()) {

View File

@ -142,13 +142,13 @@ public class SignatureFragment extends DataBindingFragment {
payDetail.setESignBlob(blob); payDetail.setESignBlob(blob);
sharedViewModel.payDetail.setValue(payDetail); sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.updateESignBlob(payDetail.voucherNo, blob); sharedViewModel.updateESignBlob(payDetail.voucherNo, blob);
EReceiptCardRequest request = EReceiptHelper.buildEReceiptCardReceipt( // EReceiptCardRequest request = EReceiptHelper.buildEReceiptCardReceipt(
requireContext(), // requireContext(),
payDetail, // payDetail,
true, // true,
BaseErrorCode.getCode(payDetail.getTradeAnswerCode()), // BaseErrorCode.getCode(payDetail.getTradeAnswerCode()),
null); // null);
sharedViewModel.pushReceiptWithImage(request, blob); // sharedViewModel.pushReceiptWithImage(request, blob);
} }
public class ClickEvent { public class ClickEvent {

View File

@ -328,7 +328,6 @@ public class SettlementTransactionFragment extends DataBindingFragment implement
settlementViewModel.pre_breakdown.setValue(buildBreakdownText(mmkBucket.preCount, mmkBucket.preAmount, usdBucket.preCount, usdBucket.preAmount)); settlementViewModel.pre_breakdown.setValue(buildBreakdownText(mmkBucket.preCount, mmkBucket.preAmount, usdBucket.preCount, usdBucket.preAmount));
settlementViewModel.refund_breakdown.setValue(buildBreakdownText(mmkBucket.refundCount, mmkBucket.refundAmount, usdBucket.refundCount, usdBucket.refundAmount)); settlementViewModel.refund_breakdown.setValue(buildBreakdownText(mmkBucket.refundCount, mmkBucket.refundAmount, usdBucket.refundCount, usdBucket.refundAmount));
settlementViewModel.ca_breakdown.setValue(buildBreakdownText(mmkBucket.caCount, mmkBucket.caAmount, usdBucket.caCount, usdBucket.caAmount)); settlementViewModel.ca_breakdown.setValue(buildBreakdownText(mmkBucket.caCount, mmkBucket.caAmount, usdBucket.caCount, usdBucket.caAmount));
if(sharedViewModel.getTransMenu().getValue() == TransMenu.REVIEW_BATCH) { if(sharedViewModel.getTransMenu().getValue() == TransMenu.REVIEW_BATCH) {
SettleData settleData = new SettleData(saleCount,saleAmount,preCount,preAmount,refundCount,refundAmount,caCount,caAmount); SettleData settleData = new SettleData(saleCount,saleAmount,preCount,preAmount,refundCount,refundAmount,caCount,caAmount);
TradeData tradeData = Params.newTrade(false); TradeData tradeData = Params.newTrade(false);
@ -384,6 +383,11 @@ public class SettlementTransactionFragment extends DataBindingFragment implement
} }
} }
private String buildBreakdownText(int mmkCount, long mmkAmount, int usdCount, long usdAmount) {
return "MMK " + mmkCount + " / " + POSUtil.getInstance().getDecimalAmountSeparatorFormat(mmkAmount)
+ " USD " + usdCount + " / " + POSUtil.getInstance().getDecimalAmountSeparatorFormat(usdAmount);
}
private CurrencyBucket getCurrencyBucket(String currencyCode) { private CurrencyBucket getCurrencyBucket(String currencyCode) {
return "USD".equals(normalizeCurrency(currencyCode)) ? usdBucket : mmkBucket; return "USD".equals(normalizeCurrency(currencyCode)) ? usdBucket : mmkBucket;
} }
@ -431,11 +435,6 @@ public class SettlementTransactionFragment extends DataBindingFragment implement
} }
} }
private String buildBreakdownText(int mmkCount, long mmkAmount, int usdCount, long usdAmount) {
return "MMK " + mmkCount + " / " + POSUtil.getInstance().getDecimalAmountSeparatorFormat(mmkAmount)
+ " USD " + usdCount + " / " + POSUtil.getInstance().getDecimalAmountSeparatorFormat(usdAmount);
}
private String normalizeCurrency(String currencyCode) { private String normalizeCurrency(String currencyCode) {
if (currencyCode == null) { if (currencyCode == null) {
return "MMK"; return "MMK";

View File

@ -11,8 +11,8 @@
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 33, "versionCode": 39,
"versionName": "2.13-uat", "versionName": "2.34-uat",
"outputFile": "app-uat-release.apk" "outputFile": "app-uat-release.apk"
} }
], ],

View File

@ -116,7 +116,7 @@ public class EmvParamOperation extends ParamOperation{
processPayWave(); processPayWave();
processPayPass(); processPayPass();
processQuickPass(); processQuickPass();
processJSpeedy(); // processJSpeedy();
processMPUCtls(); processMPUCtls();
processChips(); processChips();
processRids(); processRids();

View File

@ -530,7 +530,7 @@
"currencyCode": "0104" "currencyCode": "0104"
}, },
{ {
"aidEnable": true, "aidEnable": false,
"aid": "A00000006510", "aid": "A00000006510",
"cardScheme": "JCB", "cardScheme": "JCB",
"applicationVersion": "0200", "applicationVersion": "0200",
@ -553,7 +553,7 @@
"currencyCode": "0104" "currencyCode": "0104"
}, },
{ {
"aidEnable": true, "aidEnable": false,
"aid": "A0000000651010", "aid": "A0000000651010",
"cardScheme": "JCB", "cardScheme": "JCB",
"applicationVersion": "0200", "applicationVersion": "0200",
@ -576,7 +576,7 @@
"currencyCode": "0104" "currencyCode": "0104"
}, },
{ {
"aidEnable": true, "aidEnable": false,
"aid": "A0000001570040", "aid": "A0000001570040",
"cardScheme": "JCB", "cardScheme": "JCB",
"applicationVersion": "0200", "applicationVersion": "0200",

View File

@ -214,31 +214,16 @@ public abstract class BaseXPrint {
protected void printTransDetailSettlement(PayDetail payDetail) { protected void printTransDetailSettlement(PayDetail payDetail) {
SettleData settleData = payDetail.getSettleDataObj(); SettleData settleData = payDetail.getSettleDataObj();
List<CardSettleData> cardSettleDataList = payDetail.getCardSettleData(); print3ColumnsString("TRX TYPE ", "CNT", "AMOUNT");
CurrencySettlementSummary mmkSummary = new CurrencySettlementSummary(); print3ColumnsString("SALE ", countStringFormat(settleData.getSaleCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getSaleAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
CurrencySettlementSummary usdSummary = new CurrencySettlementSummary(); 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()));
if (cardSettleDataList != null && !cardSettleDataList.isEmpty()) { print3ColumnsString("CASH ADV ", countStringFormat(settleData.getCashAdvanceCount()), PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getCashAdvanceAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
for (CardSettleData cardSettleData : cardSettleDataList) { print3ColumnsString("REFUND ", countStringFormat(settleData.getRefundCount()), "- " + PrintUtils.getInstance().getSeparatorNumberFormat(settleData.getRefundAmount(), SystemParamsOperation.getInstance().getDecimalEnable()));
CurrencySettlementSummary summary = "USD".equals(normalizeSettlementCurrency(cardSettleData.getCurrencyCode())) ? usdSummary : mmkSummary; dotBreak();
applyCardSettlementSummary(summary, cardSettleData); long totalAmount = settleData.getSaleAmount() + settleData.getPreAuthCompAmount() + settleData.getCashAdvanceAmount()
} - settleData.getVoidSaleAmount() - settleData.getRefundAmount();
} else { print3ColumnsString("TOTAL ", "", PrintUtils.getInstance().getSeparatorNumberFormat(totalAmount, SystemParamsOperation.getInstance().getDecimalEnable()));
mmkSummary.saleCount = settleData.getSaleCount();
mmkSummary.saleAmount = settleData.getSaleAmount();
mmkSummary.preAuthCompCount = settleData.getPreAuthCompCount();
mmkSummary.preAuthCompAmount = settleData.getPreAuthCompAmount();
mmkSummary.refundCount = settleData.getRefundCount();
mmkSummary.refundAmount = settleData.getRefundAmount();
mmkSummary.cashAdvanceCount = settleData.getCashAdvanceCount();
mmkSummary.cashAdvanceAmount = settleData.getCashAdvanceAmount();
}
printSettlementCurrencySection("MMK", mmkSummary);
if (usdSummary.hasAnyValue()) {
emptyLine(1);
printSettlementCurrencySection("USD", usdSummary);
}
emptyLine(2); emptyLine(2);
} }

View File

@ -192,12 +192,7 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
printLogo(); printLogo();
printMerchantHeader(); printMerchantHeader();
printTransHeader(payDetail); printTransHeader(payDetail);
List<com.utsmyanmar.paylibs.model.CardSettleData> cardSettleDataList = payDetail.getCardSettleData(); printTransDetailSettlement(payDetail);
if (cardSettleDataList != null && !cardSettleDataList.isEmpty()) {
printTransDetailSettlement(payDetail, cardSettleDataList);
} else {
printTransDetailSettlement(payDetail);
}
// printTransFooterSummary(); // printTransFooterSummary();
printTransFooterForSettlement(); printTransFooterForSettlement();

View File

@ -42,10 +42,10 @@ public class SystemParamsSettings implements Serializable {
// private String tmsAddress = "https://api-tms.kbzbank.com/sirius"; // private String tmsAddress = "https://api-tms.kbzbank.com/sirius";
// private String ereceiptAddress = "https://api-tms.kbzbank.com/receipt"; // private String ereceiptAddress = "https://api-tms.kbzbank.com/receipt";
// private String tmsAddress = "https://api-tms-uat.kbzbank.com:8443/sirius"; private String tmsAddress = "https://api-tms-uat.kbzbank.com:8443/sirius";
// private String ereceiptAddress = "https://api-tms-uat.kbzbank.com:8443/receipt"; private String ereceiptAddress = "https://api-tms-uat.kbzbank.com:8443/receipt";
private String tmsAddress = "https://sirius-nest.utsmyanmar.com/api/v1"; // private String tmsAddress = "https://sirius-nest.utsmyanmar.com/api/v1";
private String ereceiptAddress = "https://receipt-nest.utsmyanmar.com"; // private String ereceiptAddress = "https://receipt-nest.utsmyanmar.com";
private String binValues = ""; private String binValues = "";
private String restrictedBinValues = ""; private String restrictedBinValues = "";