"login_abs_poll"
This commit is contained in:
parent
5be3469be3
commit
d41d746512
391
login_abs.js
Normal file
391
login_abs.js
Normal file
@ -0,0 +1,391 @@
|
||||
// bridge.js — HTTP→TCP bridge for ABS (ABS_POLL + LOG/LOGBT login) using 512-byte MFC-style frames
|
||||
// CommonJS (Node 18+)
|
||||
|
||||
const express = require("express");
|
||||
const net = require("net");
|
||||
|
||||
// ---- ABS endpoint (no server changes) ----
|
||||
const ABS_HOST = process.env.ABS_HOST || "192.0.0.14";
|
||||
const ABS_PORT = Number(process.env.ABS_PORT || 7000);
|
||||
|
||||
// ---- transaction/opcode constants (from your grep) ----
|
||||
const ABS_POLL = 178; // connectivity ping
|
||||
const LOG = 100; // login transaction code
|
||||
const LOGBT = 6013; // login opcode
|
||||
const SUCCESS = 0;
|
||||
|
||||
// ---- 512-byte framing ----
|
||||
const PKT_SIZE = 512;
|
||||
const PAYLOAD_PER_PKT = PKT_SIZE - 1;
|
||||
|
||||
const app = express();
|
||||
|
||||
// we intentionally avoid global express.json() — we accept empty bodies safely
|
||||
function readJsonBody(req) {
|
||||
return new Promise((resolve) => {
|
||||
const chunks = [];
|
||||
req.on("data", (c) => chunks.push(c));
|
||||
req.on("end", () => {
|
||||
if (!chunks.length) return resolve({});
|
||||
const txt = Buffer.concat(chunks).toString("utf8").trim();
|
||||
if (!txt) return resolve({});
|
||||
try { resolve(JSON.parse(txt)); } catch { resolve({}); }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- helpers: fixed-width strings, trimming ----------
|
||||
function writeFixedAscii(buf, offset, s, len) {
|
||||
const str = (s ?? "").toString();
|
||||
for (let i = 0; i < len; i++) {
|
||||
buf[offset + i] = i < str.length ? (str.charCodeAt(i) & 0xff) : 0x00;
|
||||
}
|
||||
return offset + len;
|
||||
}
|
||||
function readFixedAscii(buf, offset, len) {
|
||||
let end = offset;
|
||||
const max = offset + len;
|
||||
while (end < max && buf[end] !== 0) end++;
|
||||
const raw = buf.subarray(offset, end).toString("ascii");
|
||||
return { value: raw.trimEnd(), next: offset + len };
|
||||
}
|
||||
|
||||
// ---------- struct packers / parsers (little-endian) ----------
|
||||
|
||||
// sSndHeader (24 bytes)
|
||||
function packSndHeader({ nTxnCd, nOpCd = 0, nNumRecsSent = 0, nNumRecsRqrd = 0, nTxnId = 0, cBtMake = 0 }) {
|
||||
const b = Buffer.alloc(24, 0);
|
||||
let o = 0;
|
||||
b.writeInt32LE(nTxnCd, o); o += 4;
|
||||
b.writeInt32LE(nOpCd, o); o += 4;
|
||||
b.writeInt32LE(nNumRecsSent, o); o += 4;
|
||||
b.writeInt32LE(nNumRecsRqrd, o); o += 4;
|
||||
b.writeInt32LE(nTxnId, o); o += 4;
|
||||
b.writeUInt8(cBtMake & 0xff, o);
|
||||
return b;
|
||||
}
|
||||
|
||||
// sRcvHeader (18 or 24 bytes on the wire)
|
||||
function parseRcvHeaderFlexible(buf) {
|
||||
if (buf.length < 18) throw new Error(`Reply too short for RcvHeader: ${buf.length} bytes`);
|
||||
let o = 0;
|
||||
const nTxnCd = buf.readInt32LE(o); o += 4;
|
||||
const nRetCd = buf.readInt32LE(o); o += 4;
|
||||
const nNumRecs = buf.readInt32LE(o); o += 4;
|
||||
const nTxnId = buf.readInt32LE(o); o += 4;
|
||||
const cBtMake = buf.readUInt8(o);
|
||||
return { nTxnCd, nRetCd, nNumRecs, nTxnId, cBtMake, size: buf.length >= 24 ? 24 : 18 };
|
||||
}
|
||||
|
||||
// normalized 18-byte header (for display)
|
||||
function buildRcvHeader18({ nTxnCd, nRetCd, nNumRecs = 0, nTxnId = 0, cBtMake = 0 }) {
|
||||
const b = Buffer.alloc(18, 0);
|
||||
let o = 0;
|
||||
b.writeInt32LE(nTxnCd, o); o += 4;
|
||||
b.writeInt32LE(nRetCd, o); o += 4;
|
||||
b.writeInt32LE(nNumRecs, o); o += 4;
|
||||
b.writeInt32LE(nTxnId, o); o += 4;
|
||||
b.writeUInt8(cBtMake & 0xff, o);
|
||||
return b;
|
||||
}
|
||||
|
||||
// ---------- ABS 512-byte packetization ----------
|
||||
function absSend(sock, payload) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let off = 0;
|
||||
function sendNext() {
|
||||
const remaining = payload.length - off;
|
||||
const toCopy = Math.min(PAYLOAD_PER_PKT, Math.max(remaining, 0));
|
||||
const pkt = Buffer.alloc(PKT_SIZE, 0);
|
||||
pkt[0] = (off + toCopy >= payload.length) ? 48 /* '0' */ : 49 /* '1' */;
|
||||
if (toCopy > 0) payload.copy(pkt, 1, off, off + toCopy);
|
||||
off += toCopy;
|
||||
sock.write(pkt, (err) => {
|
||||
if (err) return reject(err);
|
||||
if (pkt[0] === 48) return resolve(); // last
|
||||
sendNext();
|
||||
});
|
||||
}
|
||||
sendNext();
|
||||
});
|
||||
}
|
||||
|
||||
function absRecv(sock, timeoutMs = 7000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
let buf = Buffer.alloc(0);
|
||||
const timer = timeoutMs ? setTimeout(() => done(new Error("TCP timeout")), timeoutMs) : null;
|
||||
|
||||
function done(err) {
|
||||
if (timer) clearTimeout(timer);
|
||||
sock.off("data", onData);
|
||||
if (err) reject(err);
|
||||
else resolve(Buffer.concat(chunks));
|
||||
}
|
||||
|
||||
function onData(data) {
|
||||
buf = Buffer.concat([buf, data]);
|
||||
while (buf.length >= PKT_SIZE) {
|
||||
const pkt = buf.subarray(0, PKT_SIZE);
|
||||
buf = buf.subarray(PKT_SIZE);
|
||||
const flag = pkt[0];
|
||||
chunks.push(pkt.subarray(1));
|
||||
if (flag === 48) return done(); // '0'
|
||||
}
|
||||
}
|
||||
|
||||
sock.on("data", onData);
|
||||
sock.on("error", (e) => done(e));
|
||||
sock.on("end", () => done(new Error("Socket ended before final '0' packet")));
|
||||
});
|
||||
}
|
||||
|
||||
async function absRoundtrip(payload, timeoutMs = 7000) {
|
||||
const sock = new net.Socket();
|
||||
await new Promise((res, rej) => sock.connect(ABS_PORT, ABS_HOST, res).once("error", rej));
|
||||
try {
|
||||
await absSend(sock, payload);
|
||||
const replyConcat = await absRecv(sock, timeoutMs);
|
||||
sock.end();
|
||||
return replyConcat;
|
||||
} catch (e) {
|
||||
try { sock.destroy(); } catch {}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- MFC-style password "encryption" ----------
|
||||
/*
|
||||
Mirrors the MFC EncryptPassword():
|
||||
- key = first digit of current UTC seconds (0..5, or 0..9 if seconds >= 100 ever, but it's 0..5)
|
||||
- for each input char: interpret as digit (non-digit -> 0), add key, keep ones digit
|
||||
- append key at the end
|
||||
Result length = input.length + 1 (cPasswd field in sSndLog is 11 bytes, so 10-digit inputs fit).
|
||||
*/
|
||||
function encryptPasswordLikeMFC(plain) {
|
||||
const sec = new Date().getUTCSeconds(); // GetSystemTime (UTC) seconds
|
||||
const key = Number(String(sec)[0] || "0"); // first digit
|
||||
let out = "";
|
||||
const s = (plain ?? "").toString();
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
const ch = s[i];
|
||||
const d = (ch >= "0" && ch <= "9") ? (ch.charCodeAt(0) - 48) : 0; // atoi on non-digit -> 0
|
||||
out += String((d + key) % 10);
|
||||
}
|
||||
return { enc: out + String(key), key, utcSeconds: sec };
|
||||
}
|
||||
|
||||
// ---------- SND/RCV: LOGIN ----------
|
||||
|
||||
// sSndLog (38 bytes): cUsrId[5], cOpCardCd[17], cPasswd[11], cBtId[5]
|
||||
function packSndLog({ usrId = "", opCard, passwd, btId }) {
|
||||
const b = Buffer.alloc(38, 0);
|
||||
let o = 0;
|
||||
o = writeFixedAscii(b, o, usrId, 5);
|
||||
o = writeFixedAscii(b, o, opCard, 17);
|
||||
o = writeFixedAscii(b, o, passwd, 11);
|
||||
o = writeFixedAscii(b, o, btId, 5);
|
||||
return b;
|
||||
}
|
||||
|
||||
// sRcvLog parser (follows sRcvHeader if nRetCd == 0)
|
||||
function parseRcvLog(buf, offset = 0) {
|
||||
let o = offset;
|
||||
const t1 = readFixedAscii(buf, o, 20); const cDateTime = t1.value; o = t1.next;
|
||||
const t2 = readFixedAscii(buf, o, 31); const cUsrNm = t2.value; o = t2.next;
|
||||
const t3 = readFixedAscii(buf, o, 5 ); const cUsrId = t3.value; o = t3.next;
|
||||
const t4 = readFixedAscii(buf, o, 31); const cSupNm = t4.value; o = t4.next;
|
||||
const t5 = readFixedAscii(buf, o, 5 ); const cSupId = t5.value; o = t5.next;
|
||||
const t6 = readFixedAscii(buf, o, 5 ); const cUsrTyp = t6.value; o = t6.next;
|
||||
|
||||
function rf() { const v = buf.readFloatLE(o); o += 4; return v; }
|
||||
function ri() { const v = buf.readInt32LE(o); o += 4; return v; }
|
||||
|
||||
const fOpenBal = rf();
|
||||
const fTktSalesByVoucher = rf();
|
||||
const fTktSalesByCash = rf();
|
||||
const fTktSalesByMemCard = rf();
|
||||
const nTktSalesByVoucherCount = ri();
|
||||
const nTktSalesByCashCount = ri();
|
||||
const nTktSalesByMemCardCount = ri();
|
||||
const fPayoutByVoucher = rf();
|
||||
const fPayoutByCash = rf();
|
||||
const fPayoutByMemCard = rf();
|
||||
const nPayoutByVoucherCount = ri();
|
||||
const nPayoutByCashCount = ri();
|
||||
const nPayoutByMemCardCount = ri();
|
||||
const fCancelByVoucher = rf();
|
||||
const fCancelByCash = rf();
|
||||
const fCancelByMemCard = rf();
|
||||
const nCancelByVoucherCount = ri();
|
||||
const nCancelByCashCount = ri();
|
||||
const nCancelByMemCardCount = ri();
|
||||
const fDeposit = rf();
|
||||
const fWithdrawAmt = rf();
|
||||
const fVoucherSales = rf();
|
||||
const fVoucherEncash = rf();
|
||||
const fCloseBal = rf();
|
||||
const fSaleTarget = rf();
|
||||
|
||||
return {
|
||||
cDateTime, cUsrNm, cUsrId, cSupNm, cSupId, cUsrTyp,
|
||||
fOpenBal, fTktSalesByVoucher, fTktSalesByCash, fTktSalesByMemCard,
|
||||
nTktSalesByVoucherCount, nTktSalesByCashCount, nTktSalesByMemCardCount,
|
||||
fPayoutByVoucher, fPayoutByCash, fPayoutByMemCard,
|
||||
nPayoutByVoucherCount, nPayoutByCashCount, nPayoutByMemCardCount,
|
||||
fCancelByVoucher, fCancelByCash, fCancelByMemCard,
|
||||
nCancelByVoucherCount, nCancelByCashCount, nCancelByMemCardCount,
|
||||
fDeposit, fWithdrawAmt, fVoucherSales, fVoucherEncash,
|
||||
fCloseBal, fSaleTarget,
|
||||
size: o - offset
|
||||
};
|
||||
}
|
||||
|
||||
// ---------- routes ----------
|
||||
app.get("/", (_req, res) => res.send("ABS bridge is up"));
|
||||
|
||||
app.get("/health", (_req, res) => {
|
||||
const s = new net.Socket();
|
||||
s.setTimeout(1500);
|
||||
s.connect(ABS_PORT, ABS_HOST, () => { s.destroy(); res.json({ ok: true, target: `${ABS_HOST}:${ABS_PORT}` }); });
|
||||
s.on("timeout", () => { s.destroy(); res.status(504).json({ ok: false, error: "TCP timeout" }); });
|
||||
s.on("error", (e) => { s.destroy(); res.status(502).json({ ok: false, error: String(e) }); });
|
||||
});
|
||||
|
||||
// ---------- ABS_POLL ----------
|
||||
app.post("/abs/poll", async (req, res) => {
|
||||
try {
|
||||
const body = await readJsonBody(req);
|
||||
const btMake = (typeof body.btMake === "string")
|
||||
? body.btMake.charCodeAt(0)
|
||||
: (Number.isFinite(body.btMake) ? (body.btMake|0) : 0x00);
|
||||
|
||||
const header = packSndHeader({
|
||||
nTxnCd: ABS_POLL, nOpCd: 0, nNumRecsSent: 0, nNumRecsRqrd: 0, nTxnId: 0, cBtMake: btMake
|
||||
});
|
||||
|
||||
const reply = await absRoundtrip(header, 7000);
|
||||
const headerSlice = reply.subarray(0, Math.min(reply.length, 24));
|
||||
const parsed = parseRcvHeaderFlexible(headerSlice);
|
||||
|
||||
const success = parsed.nRetCd === SUCCESS;
|
||||
const normalized = {
|
||||
nTxnCd: parsed.nTxnCd || ABS_POLL,
|
||||
nRetCd: parsed.nRetCd,
|
||||
nNumRecs: parsed.nNumRecs,
|
||||
nTxnId: parsed.nTxnId,
|
||||
cBtMake: parsed.cBtMake,
|
||||
size: 18
|
||||
};
|
||||
|
||||
res.json({
|
||||
ok: true,
|
||||
target: `${ABS_HOST}:${ABS_PORT}`,
|
||||
sentHeaderHex: header.toString("hex"),
|
||||
replyBytes: reply.length,
|
||||
replyHexFirst64: reply.subarray(0, 64).toString("hex"),
|
||||
parsedRcvHeaderRaw: parsed,
|
||||
normalizedRcvHeader: normalized,
|
||||
normalizedHeaderHex: buildRcvHeader18(normalized).toString("hex"),
|
||||
success
|
||||
});
|
||||
} catch (e) {
|
||||
res.status(502).json({ ok: false, error: String(e) });
|
||||
}
|
||||
});
|
||||
|
||||
// ---------- LOGIN ----------
|
||||
// POST /login
|
||||
// Body (JSON):
|
||||
// {
|
||||
// "opCard": "OPCARD17", // REQUIRED (<=17)
|
||||
// "password": "1234567890", // plain 0–9; will be encrypted for you
|
||||
// // OR: "passwordEnc": "45673" // already-encrypted string (skip client encryption)
|
||||
// "btId": "0483", // REQUIRED (<=5)
|
||||
// "usrId": "", // optional (<=5)
|
||||
// "btMake": 0 // optional: numeric or single-char string
|
||||
// }
|
||||
app.post("/login", async (req, res) => {
|
||||
try {
|
||||
const body = await readJsonBody(req);
|
||||
|
||||
const opCard = (body.opCard ?? "").toString();
|
||||
const btId = (body.btId ?? "").toString();
|
||||
const usrId = (body.usrId ?? "").toString();
|
||||
|
||||
const plain = (body.password ?? "");
|
||||
const givenEnc = (body.passwordEnc ?? "");
|
||||
|
||||
if (!opCard || !btId || (!plain && !givenEnc)) {
|
||||
return res.status(400).json({ ok: false, error: "Missing opCard, btId, and either password or passwordEnc" });
|
||||
}
|
||||
|
||||
// Encrypt if only plain is supplied (MFC-compatible)
|
||||
let encInfo = null;
|
||||
let passwd = givenEnc ? String(givenEnc) : (encInfo = encryptPasswordLikeMFC(String(plain))).enc;
|
||||
|
||||
// C field sizes: cPasswd[11] – warn if plain > 10 (enc becomes > 11)
|
||||
if (!givenEnc && String(plain).length > 10) {
|
||||
// still send (field will truncate), but tell caller
|
||||
}
|
||||
|
||||
const btMake = (typeof body.btMake === "string")
|
||||
? body.btMake.charCodeAt(0)
|
||||
: (Number.isFinite(body.btMake) ? (body.btMake|0) : 0x00);
|
||||
|
||||
const sndHeader = packSndHeader({
|
||||
nTxnCd: LOG, nOpCd: LOGBT, nNumRecsSent: 1, nNumRecsRqrd: 1, nTxnId: 0, cBtMake: btMake
|
||||
});
|
||||
const sndLog = packSndLog({ usrId, opCard, passwd, btId });
|
||||
const sendBuf = Buffer.concat([sndHeader, sndLog]);
|
||||
|
||||
const reply = await absRoundtrip(sendBuf, 10000);
|
||||
|
||||
const hdrSlice = reply.subarray(0, Math.min(reply.length, 24));
|
||||
const rcvHeader = parseRcvHeaderFlexible(hdrSlice);
|
||||
|
||||
const json = {
|
||||
ok: true,
|
||||
target: `${ABS_HOST}:${ABS_PORT}`,
|
||||
sentHeaderHex: sndHeader.toString("hex"),
|
||||
sentBodyHex: sndLog.toString("hex"),
|
||||
replyBytes: reply.length,
|
||||
replyHexFirst64: reply.subarray(0, 64).toString("hex"),
|
||||
rcvHeaderRaw: rcvHeader,
|
||||
success: rcvHeader.nRetCd === SUCCESS
|
||||
};
|
||||
|
||||
// include encryption debug (non-sensitive) if we did it here
|
||||
if (!givenEnc && encInfo) {
|
||||
json.encryption = {
|
||||
used: true,
|
||||
utcSeconds: encInfo.utcSeconds,
|
||||
key: encInfo.key,
|
||||
encPasswd: passwd
|
||||
};
|
||||
} else if (givenEnc) {
|
||||
json.encryption = { used: false, providedPasswordEnc: true };
|
||||
}
|
||||
|
||||
const bodyOffset = rcvHeader.size; // 18 or 24
|
||||
if (json.success && reply.length >= bodyOffset + 20) {
|
||||
try {
|
||||
const log = parseRcvLog(reply, bodyOffset);
|
||||
json.log = log;
|
||||
} catch (e) {
|
||||
json.parseLogError = String(e);
|
||||
}
|
||||
}
|
||||
|
||||
res.json(json);
|
||||
} catch (e) {
|
||||
res.status(502).json({ ok: false, error: String(e) });
|
||||
}
|
||||
});
|
||||
|
||||
// ---- start HTTP server ----
|
||||
const HTTP_PORT = Number(process.env.HTTP_BRIDGE_PORT || 8080);
|
||||
app.listen(HTTP_PORT, () => {
|
||||
console.log(`ABS HTTP bridge listening on :${HTTP_PORT}`);
|
||||
console.log(`Target ABS server: ${ABS_HOST}:${ABS_PORT}`);
|
||||
});
|
||||
114
login_logs.json
Normal file
114
login_logs.json
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "3dd79a7d-47b7-4f2a-86b9-e35f48b54bb3",
|
||||
"name": "login_abs",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
"_exporter_id": "46024117",
|
||||
"_collection_link": "https://johnlogan-6539403.postman.co/workspace/john-logan's-Workspace~cde4aa19-228c-45b3-88cb-692cf4289226/collection/46024117-3dd79a7d-47b7-4f2a-86b9-e35f48b54bb3?action=share&source=collection_link&creator=46024117"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "http://localhost:8080/login",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"opCard\": \"021804111066\",\n \"password\": \"0660000\",\n \"btId\": \"0483\",\n \"usrId\": \"\",\n \"btMake\": \"btmake\"\n}\n",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "http://localhost:8080/login",
|
||||
"protocol": "http",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8080",
|
||||
"path": [
|
||||
"login"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "http://localhost:8080/login",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"opCard\": \"021804111066\",\n \"password\": \"0660000\",\n \"btId\": \"0483\",\n \"usrId\": \"\",\n \"btMake\": \"btmake\"\n}\n",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "http://localhost:8080/login",
|
||||
"protocol": "http",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8080",
|
||||
"path": [
|
||||
"login"
|
||||
]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "",
|
||||
"header": [
|
||||
{
|
||||
"key": "X-Powered-By",
|
||||
"value": "Express"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"key": "Content-Length",
|
||||
"value": "1189"
|
||||
},
|
||||
{
|
||||
"key": "ETag",
|
||||
"value": "W/\"4a5-+Kk5viaxNtYc9xJLAnpb48uH9QQ\""
|
||||
},
|
||||
{
|
||||
"key": "Date",
|
||||
"value": "Wed, 20 Aug 2025 11:07:59 GMT"
|
||||
},
|
||||
{
|
||||
"key": "Connection",
|
||||
"value": "keep-alive"
|
||||
},
|
||||
{
|
||||
"key": "Keep-Alive",
|
||||
"value": "timeout=5"
|
||||
}
|
||||
],
|
||||
"cookie": [],
|
||||
"body": "{\n \"ok\": true,\n \"target\": \"192.0.0.14:7000\",\n \"sentHeaderHex\": \"640000007d17000001000000010000000000000062000000\",\n \"sentBodyHex\": \"0000000000303231383034313131303636000000000035313135353535350000003034383300\",\n \"replyBytes\": 511,\n \"replyHexFirst64\": \"6400000000000000020000000000000062000000323032352f30382f32302f31363a33373a33350052454e554b41505241534144202e4b000000000000000000\",\n \"rcvHeaderRaw\": {\n \"nTxnCd\": 100,\n \"nRetCd\": 0,\n \"nNumRecs\": 2,\n \"nTxnId\": 0,\n \"cBtMake\": 98,\n \"size\": 24\n },\n \"success\": true,\n \"encryption\": {\n \"used\": true,\n \"utcSeconds\": 59,\n \"key\": 5,\n \"encPasswd\": \"51155555\"\n },\n \"log\": {\n \"cDateTime\": \"/08/20/16:37:35\",\n \"cUsrNm\": \"KAPRASAD .K\",\n \"cUsrId\": \"\",\n \"cSupNm\": \"\",\n \"cSupId\": \"\",\n \"cUsrTyp\": \"\",\n \"fOpenBal\": 0,\n \"fTktSalesByVoucher\": 0,\n \"fTktSalesByCash\": 0,\n \"fTktSalesByMemCard\": 0,\n \"nTktSalesByVoucherCount\": 0,\n \"nTktSalesByCashCount\": 0,\n \"nTktSalesByMemCardCount\": 0,\n \"fPayoutByVoucher\": 0,\n \"fPayoutByCash\": 0,\n \"fPayoutByMemCard\": 0,\n \"nPayoutByVoucherCount\": 0,\n \"nPayoutByCashCount\": 0,\n \"nPayoutByMemCardCount\": 0,\n \"fCancelByVoucher\": 0,\n \"fCancelByCash\": 0,\n \"fCancelByMemCard\": 0,\n \"nCancelByVoucherCount\": 0,\n \"nCancelByCashCount\": 0,\n \"nCancelByMemCardCount\": 0,\n \"fDeposit\": 0,\n \"fWithdrawAmt\": 0,\n \"fVoucherSales\": 0,\n \"fVoucherEncash\": 0,\n \"fCloseBal\": 0,\n \"fSaleTarget\": 0,\n \"size\": 197\n }\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user