poster-prro-kit
Version:
Цей Kit призначений для роботи з PRRO, а саме для генерації XML документів для податкової, генерації фіскальних чеків для термопринтерів, генерації документів для PRRO в офлайн режимі, розрахунку податків та генераціі посилання на фіскальний чек в кабін
361 lines (327 loc) • 9.59 kB
JavaScript
import {
DOC_SUBTYPE_RETURN_SELL,
DOC_SUBTYPE_SELL,
DOC_TYPE_PRODUCT,
PAYMENT_CODE_CARD,
PAYMENT_CODE_CASH,
PAYMENT_TYPE_CARD,
PAYMENT_TYPE_TITLE_CARD,
PAYMENT_TYPE_TITLE_CASH,
} from "../const/fiscal.js";
import {
DOCUMENT_TYPE_RECEIPT,
DOCUMENT_TYPE_RETURN_RECEIPT,
} from "../const/request.js";
import {
cashSumDecimalRounding,
formatToFixedDecimal,
} from "../../../helpers/round.js";
import {
addVersionInEntities,
getDiscount,
getDiscountTotal,
getProductSum,
hasProductBarcode,
hasProductMarking,
updateProductsWithValidTaxes,
updateTaxesWithValidVAT,
} from "../helpers/xmlGenerator.js";
import {
getCashboxFields,
getCashierFields,
getDateTimeFields,
getDoctype,
getDocumentNumberFields,
getOfflineFields,
getOrganizationFields,
getRowNum,
getTestingModeFields,
getUIDFields,
getVersionFields,
rowsToMapper,
} from "./commonXMLTagGenerator.js";
import {
convertKopecksToGrivnas,
convertGramsToKg,
getPaymentSum,
getReceiptTotal,
getTaxSum,
getTaxTurnover,
getTaxSourceSum,
getTaxTurnoverDiscount,
getRoundSum,
} from "../../../helpers/centsFormat.js";
const getPaymentDetails = (type) => {
const paymentType = {
cash: {
PAYFORMCD: PAYMENT_CODE_CASH,
PAYFORMNM: PAYMENT_TYPE_TITLE_CASH,
},
card: {
PAYFORMCD: PAYMENT_CODE_CARD,
PAYFORMNM: PAYMENT_TYPE_TITLE_CARD,
},
};
return paymentType[type];
};
const getBarcodeBlock = (product) => {
// eslint-disable-next-line no-magic-numbers
if (product.version >= 5 && product.barcodes?.length) {
return { BARCODE: product.barcodes };
}
if (hasProductBarcode(product)) {
const { barcodes } = product;
return { BARCODE: barcodes.join(" ") };
}
return {};
};
const getDiscountBlock = (product) => {
const { discount } = product;
if (discount) {
return {
DISCOUNTTYPE: 0,
DISCOUNTSUM: formatToFixedDecimal(getDiscount(product)),
};
}
return {};
};
const getExciseLabelsBlock = (product) => {
if (hasProductMarking(product)) {
const { marking } = product;
return { EXCISELABELS: rowsToMapper(marking, exciseLabelMapper) };
}
return {};
};
const exciseLabelMapper = (exciseLabel, index) => {
return {
$: getRowNum(index),
EXCISELABEL: exciseLabel,
};
};
const sumField = (sstData) => {
// eslint-disable-next-line no-magic-numbers
if (sstData.requestVersion >= 4) {
return sstData.sum ? { SUM: sstData.sum } : {};
}
return sstData.amount || sstData.sum
? { SUM: sstData.amount || sstData.sum }
: {};
};
const getPosTransDateField = ({ dateTime }) =>
dateTime ? { POSTRANSDATE: dateTime } : {};
const paySysMapper = (requestVersion) => (sstData, index) => {
// eslint-disable-next-line no-magic-numbers
if (requestVersion >= 4) {
return {
$: getRowNum(index),
NAME: sstData.paymentSystem,
ACQUIRENM: sstData.merchant,
ACQUIRETRANSID: sstData.rrn,
...getPosTransDateField(sstData),
DEVICEID: sstData.terminalId,
EPZDETAILS: sstData.cardNumber,
AUTHCD: sstData.authCode,
...sumField(sstData),
};
}
return {
$: getRowNum(index),
NAME: sstData.paymentSystem || sstData.paymentSystemName,
ACQUIRENM: sstData.merchant || sstData.merchantId,
ACQUIRETRANSID: sstData.rrn,
...getPosTransDateField(sstData),
DEVICEID: sstData.terminalId || sstData.terminal,
EPZDETAILS: sstData.pan || sstData.cardNumber,
AUTHCD: sstData.authCode,
...sumField(sstData),
};
};
const getPaySysBlock = (payment) => {
if (!payment.sstData) {
return {};
}
const sstData = Array.isArray(payment.sstData)
? payment.sstData
: [payment.sstData];
return { PAYSYS: rowsToMapper(sstData, paySysMapper(payment.version)) };
};
const paymentMapper = (payment, index) => {
const { PAYFORMCD, PAYFORMNM } = getPaymentDetails(payment.type);
const SUM =
PAYFORMNM === PAYMENT_TYPE_TITLE_CASH
? formatToFixedDecimal(
getPaymentSum({
...payment,
sum: cashSumDecimalRounding(payment.sum),
}),
)
: formatToFixedDecimal(getPaymentSum(payment));
const PAYSYSBLOCK = getPaySysBlock(payment);
// eslint-disable-next-line no-magic-numbers
const PROVIDEDBLOCK = payment.version >= 3 ? {} : { PROVIDED: SUM };
return {
$: getRowNum(index),
PAYFORMCD,
PAYFORMNM,
SUM,
...PROVIDEDBLOCK,
...PAYSYSBLOCK,
};
};
const productMapper = (product, index) => {
const {
id: CODE,
taxPrograms,
price,
count,
uktzed,
name: NAME,
unit,
} = product;
const LETTERS = taxPrograms ? { LETTERS: taxPrograms } : {};
const PRICE = formatToFixedDecimal(convertKopecksToGrivnas(price));
const AMOUNT = convertGramsToKg(count);
const COST = formatToFixedDecimal(getProductSum(product));
const BARCODEBLOCK = getBarcodeBlock(product);
const UKTZED = uktzed ? { UKTZED: uktzed } : {};
const UNITNM = !unit ? {} : { UNITNM: unit };
const DISCOUNTBLOCK = getDiscountBlock(product);
const EXCISELABELSBLOCK = getExciseLabelsBlock(product);
return {
$: getRowNum(index),
CODE,
...BARCODEBLOCK,
...UKTZED,
NAME,
...UNITNM,
AMOUNT,
PRICE,
...LETTERS,
COST,
...DISCOUNTBLOCK,
...EXCISELABELSBLOCK,
};
};
const taxesMapper = (tax, index) => {
const { type: TYPE, name: NAME, program: LETTER, percent } = tax;
const PRC = formatToFixedDecimal(percent);
const TURNOVER = formatToFixedDecimal(getTaxTurnover(tax));
const TURNOVERDISCOUNT = formatToFixedDecimal(getTaxTurnoverDiscount(tax));
const SUM = formatToFixedDecimal(getTaxSum(tax));
const SOURCESUM = formatToFixedDecimal(getTaxSourceSum(tax));
return {
$: getRowNum(index),
TYPE,
NAME,
LETTER,
PRC,
TURNOVER,
TURNOVERDISCOUNT,
SOURCESUM,
SUM,
};
};
const isReceipt = (orderData) => orderData.type === DOCUMENT_TYPE_RECEIPT;
const isReturnReceipt = (item) => item.type === DOCUMENT_TYPE_RETURN_RECEIPT;
const getReceiptHeader = (operationData) => {
const { cashboxData, dateTime } = operationData;
const operationSum = getTotal(operationData).SUM;
return {
...getTypeFields(operationData),
...getUIDFields(operationData),
...getOrganizationFields(cashboxData),
...getDateTimeFields(dateTime),
...getDocumentNumberFields(cashboxData),
...getCashboxFields(cashboxData),
...getReturnReceiptFields(operationData),
...getCashierFields(operationData),
...getVersionFields(),
...getOfflineFields({ operationData, operationSum }),
...getTestingModeFields(cashboxData),
};
};
const getTypeFields = (orderData) =>
getDoctype(
DOC_TYPE_PRODUCT,
isReceipt(orderData) ? DOC_SUBTYPE_SELL : DOC_SUBTYPE_RETURN_SELL,
);
const getReturnReceiptFields = (orderData) => {
const { documentFiscalId } = orderData;
return documentFiscalId && isReturnReceipt(orderData)
? { ORDERRETNUM: documentFiscalId }
: {};
};
const getTotal = (orderData) => {
const total = getReceiptTotal(orderData);
const roundSum = getRoundSum(orderData);
const discountTotal = getDiscountTotal(orderData.products);
const DISCOUNTSUM = discountTotal
? { DISCOUNTSUM: formatToFixedDecimal(discountTotal) }
: {};
// Не використовуємо поки roundSum є складником discount поля, треба будет ПЕРЕРОБИТИ коли виправлять discount
// Переробити - це не розраховувати самим, а акумулювати з полів продуктів
// if (roundDiff) {
// return {
// SUM: formatToFixedDecimal(total),
// RNDSUM: formatToFixedDecimal(roundDiff),
// NORNDSUM: formatToFixedDecimal(total - roundDiff),
// ...DISCOUNTSUM,
// };
// }
const negativeMultiplier = -1;
const ROUNDSUMBLOCK = roundSum
? {
SUM: formatToFixedDecimal(total),
RNDSUM: formatToFixedDecimal(negativeMultiplier * roundSum),
NORNDSUM: formatToFixedDecimal(total - roundSum),
}
: {};
return {
SUM: formatToFixedDecimal(total),
...ROUNDSUMBLOCK,
...DISCOUNTSUM,
};
};
const mixinSstDataToPayments = (payments, sstData) => {
return payments.map((payment) => ({
...payment,
...(sstData && payment.type === PAYMENT_TYPE_CARD ? { sstData } : {}),
}));
};
const getReceiptDocument = (data) => {
const { products, payments, taxes, sstData, cashboxData, version = 0 } = data;
const { VATTaxList } = cashboxData;
const updatedTaxes = updateTaxesWithValidVAT(taxes, VATTaxList);
const taxesWithVersion = addVersionInEntities(updatedTaxes, version);
const productsWithTaxPrograms = updateProductsWithValidTaxes(
products,
VATTaxList,
);
const productsWithVersion = addVersionInEntities(
productsWithTaxPrograms,
version,
);
const paymentsWithSstData = mixinSstDataToPayments(payments, sstData);
const paymentsWithVersion = addVersionInEntities(
paymentsWithSstData,
version,
);
const CHECKHEAD = getReceiptHeader(data);
const CHECKTOTAL = getTotal(data);
const CHECKPAY = rowsToMapper(paymentsWithVersion, paymentMapper);
const CHECKBODY = rowsToMapper(productsWithVersion, productMapper);
const CHECKTAX =
taxesWithVersion.length === 0
? {}
: { CHECKTAX: rowsToMapper(taxesWithVersion, taxesMapper) };
return {
CHECK: {
CHECKHEAD,
CHECKTOTAL,
CHECKPAY,
...CHECKTAX,
CHECKBODY,
},
};
};
export default getReceiptDocument;