diff --git a/pom.xml b/pom.xml
index fcc8224a2..1e0300dc8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,12 @@
au.com.royalpay.payment
jd-core
+
+ com.github.stuxuhai
+ jpinyin
+ 1.1.7
+
+
au.com.royalpay.payment
bestpay-core
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ATOBulkData.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ATOBulkData.java
new file mode 100644
index 000000000..c36d06253
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ATOBulkData.java
@@ -0,0 +1,93 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Create by yixian at 2018-08-30 18:38
+ */
+public class ATOBulkData {
+
+ private IntermediaryData intermediary;
+
+ public ATOBulkData(IntermediaryData intermediary) {
+ this.intermediary = intermediary;
+ }
+
+ public String outputBTTPS() {
+ StringBuilder builder = new StringBuilder();
+ intermediary.buildLine(builder);
+ return builder.toString();
+ }
+
+ public static class IntermediaryData implements ATOBulkLine {
+ private final String identifier = "IDENTREGISTER";
+ private final String version = "BTPSV001.0";
+ private String abn;
+ private Date createTime;
+ private String fileReference;
+ private String name;
+ private ContactInfo contactInfo;
+ private AddressInfo address;
+ private List reportingParties = new ArrayList<>();
+
+ public IntermediaryData(String abn, String name, String fileReference) {
+ this.abn = abn;
+ this.fileReference = fileReference;
+ this.name = name;
+ this.createTime = new Date();
+ }
+
+ public IntermediaryData setContactInfo(ContactInfo contactInfo) {
+ this.contactInfo = contactInfo;
+ return this;
+ }
+
+ public IntermediaryData setAddress(AddressInfo address) {
+ this.address = address;
+ return this;
+ }
+
+ @Override
+ public StringBuilder buildLine(StringBuilder builder) {
+ StringBuilder line = new StringBuilder();
+ appendNParam(line, 1400, 4);
+ appendAParam(line, identifier, 13);
+ appendAParam(line, version, 10);
+ appendAParam(line, abn, 11);
+ appendDTParam(line, createTime, 28);
+ appendAParam(line, fileReference, 16);
+ appendAParam(line, name, 200);
+ appendAParam(line, contactInfo.getContactName(), 40);
+ appendNParam(line, contactInfo.getPhoneAreaCode(), 2);
+ appendAParam(line, contactInfo.getPhoneNumber(), 15);
+ appendAParam(line, address.getAddress(), 38 * 2);
+ appendAParam(line, address.getSuburb(), 27);
+ appendAParam(line, address.getState(), 3);
+ appendNParam(line, address.getPostCode(), 4);
+ appendAParam(line, address.getCountry(), 50);
+ appendAParam(line, contactInfo.getEmail(), 76);
+ appendFiller(line, 825);
+
+ builder.append(line);
+
+ for (ReportingPartyData partyData : reportingParties) {
+ partyData.buildLine(builder);
+ }
+
+ TotalDataRecord total = new TotalDataRecord()
+ .setReportPartyCount(reportingParties.size())
+ .setBusinessCount(reportingParties.stream().mapToInt(ReportingPartyData::businessCount).sum())
+ .setTransactionsCount(reportingParties.stream().mapToInt(ReportingPartyData::transactionCount).sum());
+ total.buildLine(builder);
+ return builder;
+ }
+
+ public void addReportingParty(ReportingPartyData reportingParty) {
+ reportingParties.add(reportingParty);
+ }
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ATOBulkLine.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ATOBulkLine.java
new file mode 100644
index 000000000..4b11067e8
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ATOBulkLine.java
@@ -0,0 +1,70 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+import com.github.stuxuhai.jpinyin.PinyinException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.util.Date;
+
+/**
+ * Create by yixian at 2018-08-30 21:11
+ */
+public interface ATOBulkLine {
+ StringBuilder buildLine(StringBuilder builder);
+
+ default StringBuilder appendAParam(StringBuilder line, String content, int length) {
+ content = content == null ? "" : StringUtils.trim(content);
+ content = StringUtils.remove(StringUtils.remove(content, '\r'), '\n');
+ int contentCharacterLength = content.getBytes().length;
+ int contentLength = content.length();
+ if (contentCharacterLength != contentLength) {
+ try {
+ content = CharacterUtils.convertToAlphaBet(content);
+ } catch (PinyinException e) {
+ e.printStackTrace();
+ content = "";
+ }
+ }
+ content = content.replace("?", " ");
+ if (content.getBytes().length != content.length()) {
+ content = "";
+ }
+ line.append(StringUtils.left(StringUtils.rightPad(content, length, " "), length));
+ return line;
+ }
+
+ default StringBuilder appendDParam(StringBuilder line, Date date, int len) {
+ String dateStr = DateFormatUtils.format(date, "yyyyMMdd");
+ return appendAParam(line, dateStr, len);
+ }
+
+ default StringBuilder appendDTParam(StringBuilder line, Date date, int len) {
+ DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSZZ");
+ String dateStr = formatter.print(new DateTime(date));
+ return appendAParam(line, dateStr, len);
+ }
+
+ default StringBuilder appendNParam(StringBuilder line, int num, int len) {
+ line.append(StringUtils.right(StringUtils.leftPad(num + "", len, "0"), len));
+ return line;
+ }
+
+ default StringBuilder appendNParam(StringBuilder line, String num, int len) {
+ num = num == null ? "" : StringUtils.trim(num);
+ line.append(StringUtils.right(StringUtils.leftPad(num, len, "0"), len));
+ return line;
+ }
+
+ default StringBuilder appendNSParam(StringBuilder line, int num, int len) {
+ line.append(StringUtils.right(StringUtils.leftPad(num + "", len, " "), len));
+ return line;
+ }
+
+ default StringBuilder appendFiller(StringBuilder line, int len) {
+ line.append(StringUtils.repeat(" ", len)).append("\r\n");
+ return line;
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/AddressInfo.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/AddressInfo.java
new file mode 100644
index 000000000..a59de68b0
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/AddressInfo.java
@@ -0,0 +1,47 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+/**
+ * Create by yixian at 2018-08-30 21:17
+ */
+public class AddressInfo {
+ private String address;
+ private String suburb;
+ private String state;
+ private String postCode;
+ private String country;
+
+ public AddressInfo(String address, String suburb, String state, String postCode, String country) {
+ this.address = address;
+ this.suburb = suburb;
+ this.state = state;
+ if (!postCode.matches("^\\d{4}$")) {
+ postCode = "9999";
+ country = "OTH";
+ }
+ this.postCode = postCode;
+ if (country != null && country.length() != 3) {
+ country = "OTH";
+ }
+ this.country = country;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getSuburb() {
+ return suburb;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public String getPostCode() {
+ return postCode;
+ }
+
+ public String getCountry() {
+ return "AUS".equals(country) ? "" : country;
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/BusinessData.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/BusinessData.java
new file mode 100644
index 000000000..3d0498c01
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/BusinessData.java
@@ -0,0 +1,121 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Create by yixian at 2018-08-30 21:21
+ */
+public class BusinessData implements ATOBulkLine {
+ private final String identifier = "BIDR";
+ private String customer;
+ private Date paymentFacility;
+ private String surName;
+ private String firstName;
+ private String secondName;
+ private Date birth;
+ private String abn;
+ private String acn;
+ private String registeredName;
+ private String tradingName;
+ private AddressInfo address;
+ private AddressInfo postalAddress;
+ private ContactInfo contactInfo;
+ private String mchId;
+ private String category;
+ private String lodgment;
+
+ public BusinessData(Date paymentFacility, String abn, String acn, String registeredName, String tradingName, String mchId, String category, String lodgment) {
+ this.paymentFacility = paymentFacility;
+ if (abn != null) {
+ abn = abn.replaceAll("\\D", "");
+ if (abn.length() != 11) {
+ abn = "";
+ }
+ }
+ this.abn = abn;
+ if (acn != null) {
+ acn = acn.replaceAll("\\D", "");
+ if (acn.length() != 9) {
+ acn = "";
+ }
+ }
+ this.acn = acn;
+ this.registeredName = registeredName;
+ this.tradingName = tradingName;
+ this.mchId = mchId;
+ this.customer = mchId;
+ this.category = category;
+ this.lodgment = lodgment;
+ }
+
+ public BusinessData setAddress(AddressInfo address) {
+ this.address = address;
+ return this;
+ }
+
+ public BusinessData setPostalAddress(AddressInfo postalAddress) {
+ this.postalAddress = postalAddress;
+ return this;
+ }
+
+ public BusinessData setContactInfo(ContactInfo contactInfo) {
+ this.contactInfo = contactInfo;
+ return this;
+ }
+
+ private List transactions = new ArrayList<>();
+
+ @Override
+ public StringBuilder buildLine(StringBuilder builder) {
+ StringBuilder line = new StringBuilder();
+ appendNParam(line, 1400, 4);
+ appendAParam(line, identifier, 4);
+ appendAParam(line, customer, 25);
+ appendDParam(line, paymentFacility, 8);
+ appendAParam(line, surName, 40);
+ appendAParam(line, firstName, 40);
+ appendAParam(line, secondName, 40);
+ appendAParam(line, null, 8);
+ appendNParam(line, abn, 11);
+ appendNParam(line, acn, 9);
+ appendAParam(line, registeredName, 200);
+ appendAParam(line, tradingName, 200);
+ appendAParam(line, address.getAddress(), 38 * 2);
+ appendAParam(line, address.getSuburb(), 27);
+ appendAParam(line, address.getState(), 3);
+ appendNParam(line, address.getPostCode(), 4);
+ appendAParam(line, address.getCountry(), 50);
+ appendAParam(line, postalAddress.getAddress(), 38 * 2);
+ appendAParam(line, postalAddress.getSuburb(), 27);
+ appendAParam(line, postalAddress.getState(), 3);
+ appendNParam(line, postalAddress.getPostCode(), 4);
+ appendAParam(line, postalAddress.getCountry(), 50);
+ appendAParam(line, contactInfo.getContactName(), 40);
+ appendNParam(line, contactInfo.getPhoneAreaCode(), 2);
+ appendAParam(line, contactInfo.getPhoneNumber(), 15);
+ appendAParam(line, contactInfo.getEmail(), 76);
+ appendAParam(line, mchId, 20);
+ appendAParam(line, category, 100);
+ appendAParam(line, lodgment, 1);
+ appendFiller(line, 237);
+
+ builder.append(line);
+
+ for (TransactionSummaryData trans : transactions) {
+ trans.buildLine(builder);
+ }
+ return builder;
+ }
+
+ public int transactionCount() {
+ return transactions.size();
+ }
+
+ public void addTransaction(TransactionSummaryData transactionSummaryData) {
+ //todo system account number
+ transactionSummaryData.fillSystemInfo(mchId, "Merchant Acquiring System", mchId);
+ transactions.add(transactionSummaryData);
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/CharacterUtils.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/CharacterUtils.java
new file mode 100644
index 000000000..5ea6b75ad
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/CharacterUtils.java
@@ -0,0 +1,53 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+import com.github.stuxuhai.jpinyin.PinyinException;
+import com.github.stuxuhai.jpinyin.PinyinFormat;
+import com.github.stuxuhai.jpinyin.PinyinHelper;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.stream.Collectors;
+
+/**
+ * Create by yixian at 2018-09-06 9:03
+ */
+public class CharacterUtils {
+
+ public static String convertToAlphaBet(String source) throws PinyinException {
+ return convertFullWidthToHalfWidth(convertToPinYin(source));
+ }
+
+ public static String convertToPinYin(String source) throws PinyinException {
+ return PinyinHelper.convertToPinyinString(source, "", PinyinFormat.WITHOUT_TONE).toUpperCase();
+ }
+
+ public static String convertFullWidthToHalfWidth(String source) {
+ if (StringUtils.isEmpty(source)) {
+ return source;
+ }
+
+ return source.chars().map(ch -> {
+ if (ch == '\u3000') {
+ return '\u0020';
+ }
+ if (isFullWidth((char) ch)) {
+ return (char) (ch - 65248);
+ }
+ return (char) ch;
+ }).filter(CharacterUtils::isHalfWidth)
+ .mapToObj(chInt -> "" + (char) chInt).collect(Collectors.joining());
+ }
+
+ private static boolean isHalfWidth(int ch) {
+ return ch >= 32 && ch <= 126;
+ }
+
+ private static boolean isFullWidth(int ch) {
+ if (ch == '\u3000') return true;
+
+ int i = ch - 65248;
+ if (i < 32) return false;
+ return isHalfWidth((char) i);
+ }
+
+
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ContactInfo.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ContactInfo.java
new file mode 100644
index 000000000..bf2cbadf1
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ContactInfo.java
@@ -0,0 +1,88 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
+
+/**
+ * Create by yixian at 2018-08-30 21:19
+ */
+public class ContactInfo {
+ private String contactName;
+ private int phoneAreaCode;
+ private String phoneNumber;
+ private String email;
+
+ public ContactInfo(String contactName, String phoneNumber, String email) {
+ this.contactName = contactName;
+ if (isPhoneNumber(phoneNumber)) {
+ phoneNumber = phoneNumber.replace(" ", "");
+ String auPhoneNumber = formatAustralianPhoneNumber(phoneNumber);
+ if (auPhoneNumber != null) {
+ String subPhone = auPhoneNumber.substring(3);
+ if (!subPhone.startsWith("1300") && !subPhone.startsWith("1800")) {
+ this.phoneAreaCode = Integer.parseInt(subPhone.substring(0, 1));
+ if (4 == phoneAreaCode) {
+ this.phoneNumber = subPhone.substring(1, 3) + " " + subPhone.substring(3, 6) + " " + subPhone.substring(6);
+ } else {
+ this.phoneNumber = subPhone.substring(1, 5) + " " + subPhone.substring(5);
+ }
+ }
+
+ } else {
+ this.phoneNumber = formatOtherPhoneNumber(phoneNumber);
+ }
+ }
+ EmailValidator validator = new EmailValidator();
+ this.email = validator.isValid(email, null) ? email : "";
+ }
+
+ private boolean isPhoneNumber(String phoneNumber) {
+ return phoneNumber.matches("^\\+?\\d+$");
+ }
+
+ private String formatAustralianPhoneNumber(String phoneNumber) {
+ if (phoneNumber.startsWith("+61") && phoneNumber.length() == 12) {
+ return phoneNumber;
+ }
+ if (phoneNumber.startsWith("61") && phoneNumber.length() == 11) {
+ return "+" + phoneNumber;
+ }
+ if (phoneNumber.startsWith("0") && phoneNumber.length() == 10) {
+ return "+61" + phoneNumber.substring(1);
+ }
+ if (phoneNumber.length() == 9) {
+ return "+61" + phoneNumber;
+ }
+ return null;
+ }
+
+ private String formatOtherPhoneNumber(String phoneNumber) {
+ String num = phoneNumber.replace(" ", "");
+ if (num.startsWith("+")) {
+ return num.replace("+", "");
+ }
+ if (num.startsWith("00")) {
+ return num.substring(2);
+ }
+ if (num.startsWith("1") && num.length() == 11) {
+ //中国手机号
+ return "86" + num;
+ }
+ return null;
+ }
+
+ public String getContactName() {
+ return contactName;
+ }
+
+ public int getPhoneAreaCode() {
+ return phoneAreaCode;
+ }
+
+ public String getPhoneNumber() {
+ return phoneNumber;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ReportingPartyData.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ReportingPartyData.java
new file mode 100644
index 000000000..e3644757e
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/ReportingPartyData.java
@@ -0,0 +1,107 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+import org.apache.commons.lang3.time.DateUtils;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Create by yixian at 2018-08-30 21:13
+ */
+public class ReportingPartyData implements ATOBulkLine {
+ private final String identifier = "RPDR";
+ private Date periodStart;
+ private Date periodEnd;
+ private String abn;
+ private String branchNumber;
+ private String registerName;
+ private String tradingName;
+ private AddressInfo address;
+ private AddressInfo postalAddress;
+ private ContactInfo contactInfo;
+ private String softwareProductType;
+
+ private Map businesses = new HashMap<>();
+
+ public ReportingPartyData(Date periodStart, Date periodEnd, String abn, String branchNumber, String registerName, String softwareProductType) {
+ this.periodStart = DateUtils.truncate(periodStart, Calendar.DATE);
+ this.periodEnd = DateUtils.truncate(periodEnd, Calendar.DATE);
+ this.abn = abn;
+ this.branchNumber = branchNumber;
+ this.registerName = registerName;
+ this.softwareProductType = softwareProductType;
+ }
+
+ public ReportingPartyData setTradingName(String tradingName) {
+ this.tradingName = tradingName;
+ return this;
+ }
+
+ public ReportingPartyData setAddress(AddressInfo address) {
+ this.address = address;
+ return this;
+ }
+
+ public ReportingPartyData setPostalAddress(AddressInfo postalAddress) {
+ this.postalAddress = postalAddress;
+ return this;
+ }
+
+ public ReportingPartyData setContactInfo(ContactInfo contactInfo) {
+ this.contactInfo = contactInfo;
+ return this;
+ }
+
+ @Override
+ public StringBuilder buildLine(StringBuilder builder) {
+ StringBuilder line = new StringBuilder();
+ appendNParam(line, 1400, 4);
+ appendAParam(line, identifier, 4);
+ appendDParam(line, periodStart, 8);
+ appendDParam(line, periodEnd, 8);
+ appendNParam(line, abn, 11);
+ appendNParam(line, branchNumber, 3);
+ appendAParam(line, registerName, 200);
+ appendAParam(line, tradingName, 200);
+ appendAParam(line, address.getAddress(), 38 * 2);
+ appendAParam(line, address.getSuburb(), 27);
+ appendAParam(line, address.getState(), 3);
+ appendNParam(line, address.getPostCode(), 4);
+ appendAParam(line, address.getCountry(), 50);
+ appendAParam(line, postalAddress.getAddress(), 38 * 2);
+ appendAParam(line, postalAddress.getSuburb(), 27);
+ appendAParam(line, postalAddress.getState(), 3);
+ appendNParam(line, postalAddress.getPostCode(), 4);
+ appendAParam(line, postalAddress.getCountry(), 50);
+ appendAParam(line, contactInfo.getContactName(), 40);
+ appendNParam(line, contactInfo.getPhoneAreaCode(), 2);
+ appendAParam(line, contactInfo.getPhoneNumber(), 15);
+ appendAParam(line, contactInfo.getEmail(), 76);
+ appendAParam(line, softwareProductType, 80);
+ appendFiller(line, 429);
+
+ builder.append(line);
+ businesses.values().stream()
+ .filter(biz -> biz.transactionCount() > 0)
+ .forEach(biz -> biz.buildLine(builder));
+ return builder;
+ }
+
+ public int businessCount() {
+ return (int) businesses.values().stream().filter(biz -> biz.transactionCount() > 0).count();
+ }
+
+ public int transactionCount() {
+ return businesses.values().stream().mapToInt(BusinessData::transactionCount).sum();
+ }
+
+ public BusinessData findBusiness(Integer clientId) {
+ return businesses.get(clientId);
+ }
+
+ public void addBusiness(Integer clientId, BusinessData biz) {
+ businesses.put(clientId, biz);
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/TotalDataRecord.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/TotalDataRecord.java
new file mode 100644
index 000000000..4231dd587
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/TotalDataRecord.java
@@ -0,0 +1,46 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+/**
+ * Create by yixian at 2018-08-30 21:35
+ */
+public class TotalDataRecord implements ATOBulkLine {
+ private final String identifier = "FILE-TOTAL";
+ private int reportPartyCount;
+ private int businessCount;
+ private int transactionsCount;
+
+
+ public TotalDataRecord setReportPartyCount(int reportPartyCount) {
+ this.reportPartyCount = reportPartyCount;
+ return this;
+ }
+
+ public TotalDataRecord setBusinessCount(int businessCount) {
+ this.businessCount = businessCount;
+ return this;
+ }
+
+ public TotalDataRecord setTransactionsCount(int transactionsCount) {
+ this.transactionsCount = transactionsCount;
+ return this;
+ }
+
+ @Override
+ public StringBuilder buildLine(StringBuilder builder) {
+ StringBuilder line = new StringBuilder();
+ appendNParam(line, 1400, 4);
+ appendAParam(line, identifier, 10);
+ appendNParam(line, getRecords(), 10);
+ appendNParam(line, reportPartyCount, 10);
+ appendNParam(line, businessCount, 10);
+ appendNParam(line, transactionsCount, 10);
+ appendFiller(line, 1346);
+
+ builder.append(line);
+ return builder;
+ }
+
+ private int getRecords() {
+ return reportPartyCount + businessCount + transactionsCount + 2;
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/TransactionSummaryData.java b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/TransactionSummaryData.java
new file mode 100644
index 000000000..b72202d0a
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/beans/ato/TransactionSummaryData.java
@@ -0,0 +1,82 @@
+package au.com.royalpay.payment.manage.analysis.beans.ato;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.time.DateUtils;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * Create by yixian at 2018-08-30 21:30
+ */
+public class TransactionSummaryData implements ATOBulkLine {
+ private final String identifier = "TSDR";
+ private String customer;
+ private String description;
+ private String accountNumber;
+ private String settleAccountName;
+ private String settleBSB;
+ private String settleAccountNo;
+ private Date periodStart;
+ private Date periodEnd;
+ private String currency = "AUD";
+ private BigDecimal grossPayment;
+ private int transactionCount;
+ private BigDecimal totalRefund;
+ private int refundCount;
+ private BigDecimal cashoutAmount = BigDecimal.ZERO;
+ private int cashoutCount = 0;
+ private BigDecimal cashoutComponentAmount = BigDecimal.ZERO;
+
+ public TransactionSummaryData(JSONObject analysis, DateTime startOfMon, DateTime endOfMon) {
+ settleAccountName = analysis.getString("account_name");
+ settleBSB = analysis.getString("bsb_no");
+ settleAccountNo = analysis.getString("account_no").replaceAll("\\D", "");
+ periodStart = analysis.getDate("period_start");
+ periodEnd = analysis.getDate("period_end");
+ if (DateUtils.isSameDay(periodStart, periodEnd)) {
+ if (DateUtils.isSameDay(periodStart, startOfMon.toDate())) {
+ periodEnd = DateUtils.addDays(periodEnd, 1);
+ } else {
+ periodStart = DateUtils.addDays(periodStart, -1);
+ }
+ }
+ grossPayment = analysis.getBigDecimal("gross_payment");
+ transactionCount = analysis.getIntValue("pay_count");
+ totalRefund = analysis.getBigDecimal("total_refund");
+ refundCount = analysis.getIntValue("refund_count");
+ }
+
+ public void fillSystemInfo(String customer, String systemDesc, String systemAccount) {
+ this.customer = customer;
+ this.description = systemDesc;
+ this.accountNumber = systemAccount;
+ }
+
+ @Override
+ public StringBuilder buildLine(StringBuilder builder) {
+ StringBuilder line = new StringBuilder();
+ appendNParam(line, 1400, 4);
+ appendAParam(line, identifier, 4);
+ appendAParam(line, customer, 25);
+ appendAParam(line, description, 40);
+ appendAParam(line, accountNumber, 25);
+ appendAParam(line, settleAccountName, 30);
+ appendNParam(line, settleBSB, 6);
+ appendNParam(line, settleAccountNo, 30);
+ appendDParam(line, periodStart, 8);
+ appendDParam(line, periodEnd, 8);
+ appendAParam(line, currency, 3);
+ appendNParam(line, grossPayment.intValue(), 13);
+ appendNParam(line, transactionCount, 13);
+ appendNParam(line, totalRefund.intValue(), 13);
+ appendNParam(line, refundCount, 13);
+ appendNParam(line, cashoutAmount.intValue(), 13);
+ appendNParam(line, cashoutCount, 13);
+ appendNParam(line, cashoutComponentAmount.intValue(), 20);
+ appendFiller(line, 1119);
+ builder.append(line);
+ return builder;
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/core/ATOReportService.java b/src/main/java/au/com/royalpay/payment/manage/analysis/core/ATOReportService.java
new file mode 100644
index 000000000..b1b018dcd
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/core/ATOReportService.java
@@ -0,0 +1,11 @@
+package au.com.royalpay.payment.manage.analysis.core;
+
+import java.util.Date;
+
+/**
+ * Create by yixian at 2018-08-31 1:22
+ */
+public interface ATOReportService {
+
+ String exportBTTPSFile(Date from, Date to);
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/analysis/core/impls/ATOReportServiceImpl.java b/src/main/java/au/com/royalpay/payment/manage/analysis/core/impls/ATOReportServiceImpl.java
new file mode 100644
index 000000000..5ecb72367
--- /dev/null
+++ b/src/main/java/au/com/royalpay/payment/manage/analysis/core/impls/ATOReportServiceImpl.java
@@ -0,0 +1,132 @@
+package au.com.royalpay.payment.manage.analysis.core.impls;
+
+import au.com.royalpay.payment.manage.analysis.beans.ato.*;
+import au.com.royalpay.payment.manage.analysis.core.ATOReportService;
+import au.com.royalpay.payment.manage.mappers.payment.TransactionMapper;
+import au.com.royalpay.payment.manage.mappers.system.ClientMapper;
+import au.com.royalpay.payment.tools.exceptions.ServerErrorException;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.codec.Charsets;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Create by yixian at 2018-08-31 1:23
+ */
+@Service
+public class ATOReportServiceImpl implements ATOReportService {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ @Resource
+ private TransactionMapper transactionMapper;
+ @Resource
+ private ClientMapper clientMapper;
+ @Value("classpath:data/category/billbuddyindustry.json")
+ private org.springframework.core.io.Resource industryResource;
+ private Map industryMap;
+
+ @PostConstruct
+ public void loadIndustryConfiguration() throws IOException {
+ industryMap = new HashMap<>();
+ JSONArray config = JSON.parseArray(IOUtils.toString(industryResource.getInputStream(), Charsets.UTF_8));
+ for (int i = 0; i < config.size(); i++) {
+ JSONObject industry = config.getJSONObject(i);
+ loadIndustry(industry, null);
+ }
+ }
+
+ private void loadIndustry(JSONObject industry, String parentCategory) {
+ JSONArray children = industry.getJSONArray("children");
+ String category = industry.getString("category");
+ String code = industry.getString("mccCode");
+ if (category == null && parentCategory == null) {
+ throw new ServerErrorException("Industry Have No Category Code:" + code);
+ }
+ category = category == null ? parentCategory : category;
+ industryMap.put(code, category);
+ if (children != null && !children.isEmpty()) {
+ for (int i = 0; i < children.size(); i++) {
+ JSONObject subIndustry = children.getJSONObject(i);
+ loadIndustry(subIndustry, category);
+ }
+ }
+ }
+
+ @Override
+ public String exportBTTPSFile(Date from, Date to) {
+ String reference = "RPAY" + DateFormatUtils.format(new Date(), "yyyyMMddHHmm");
+ String abn = "16601619685";
+ String registerCompanyName = "Tunnel Show Pty Ltd";
+ AddressInfo companyAddr = new AddressInfo("Level 11, 15 William St", "Melbourne", "VIC", "3000", "AUS");
+ ContactInfo companyContact = new ContactInfo("Locky", "+61394488865", "info@royalpay.com.au");
+ ATOBulkData.IntermediaryData intermediary = new ATOBulkData.IntermediaryData(abn, registerCompanyName, reference)
+ .setAddress(companyAddr)
+ .setContactInfo(companyContact);
+ ATOBulkData data = new ATOBulkData(intermediary);
+
+ ReportingPartyData reportingParty = new ReportingPartyData(from, to, abn, "001", registerCompanyName, "INHOUSE Tunnel Show")
+ .setAddress(companyAddr)
+ .setPostalAddress(companyAddr)
+ .setContactInfo(companyContact)
+ .setTradingName("RoyalPay");
+ intermediary.addReportingParty(reportingParty);
+
+ DateTime fromDateTime = new DateTime(from).withTimeAtStartOfDay();
+ DateTime toDateTime = new DateTime(to).plusDays(1).withTimeAtStartOfDay();
+ DateTime startOfMon = new DateTime(fromDateTime);
+ DateTime endOfMon = new DateTime(fromDateTime.plusMonths(1).withDayOfMonth(1));
+ while (endOfMon.isBefore(toDateTime)) {
+ logger.debug("Exporting date range:" + startOfMon.toString("yyyy-MM-dd") + " ~ " + endOfMon.toString("yyyy-MM-dd"));
+ loadMonthTransactions(reportingParty, startOfMon, endOfMon);
+ startOfMon = new DateTime(endOfMon);
+ endOfMon = new DateTime(endOfMon.plusMonths(1));
+ }
+ endOfMon = new DateTime(toDateTime);
+ logger.debug("Exporting date range:" + startOfMon.toString("yyyy-MM-dd") + " ~ " + endOfMon.toString("yyyy-MM-dd"));
+ loadMonthTransactions(reportingParty, startOfMon, endOfMon);
+ logger.info("output BTTPS file");
+ return data.outputBTTPS();
+ }
+
+ private void loadMonthTransactions(ReportingPartyData reportingParty, DateTime startOfMon, DateTime endOfMon) {
+ List clients = clientMapper.listClientsWithTransactionsSettled(startOfMon.toDate(), endOfMon.toDate());
+ clients.parallelStream().forEach(clientId -> loadClientMonthTransactions(reportingParty, clientId, startOfMon, endOfMon));
+ }
+
+ private void loadClientMonthTransactions(ReportingPartyData reportingParty, Integer clientId, DateTime startOfMon, DateTime endOfMon) {
+ BusinessData biz = reportingParty.findBusiness(clientId);
+ logger.debug("Exporting date range for client[" + clientId + "]:" + startOfMon.toString("yyyy-MM-dd") + " ~ " + endOfMon.toString("yyyy-MM-dd"));
+ if (biz == null) {
+ JSONObject cli = clientMapper.findClientIgnoreInvalid(clientId);
+ AddressInfo addr = new AddressInfo(cli.getString("address"), cli.getString("suburb"), cli.getString("state"), cli.getString("postcode"), cli.getString("country"));
+ ContactInfo contact = new ContactInfo(cli.getString("contact_person"), cli.getString("contact_phone"), cli.getString("contact_email"));
+ String industry = industryMap.getOrDefault(cli.getString("royalpayindustry"), cli.getString("royalpayindustry"));
+ String clientMoniker = cli.getString("client_moniker");
+ biz = new BusinessData(cli.getDate("create_time"), cli.getString("abn"), cli.getString("acn"),
+ cli.getString("company_name"), cli.getString("short_name"), clientMoniker,
+ industry, "O")
+ .setAddress(addr)
+ .setPostalAddress(addr)
+ .setContactInfo(contact);
+ reportingParty.addBusiness(clientId, biz);
+ }
+ List analysisList = transactionMapper.analysisForATOReport(clientId, startOfMon.toDate(), endOfMon.toDate());
+ for (JSONObject analysis : analysisList) {
+ biz.addTransaction(new TransactionSummaryData(analysis, startOfMon, endOfMon));
+ }
+ }
+}
diff --git a/src/main/java/au/com/royalpay/payment/manage/dev/web/TestController.java b/src/main/java/au/com/royalpay/payment/manage/dev/web/TestController.java
index dfbeebe28..c14326cc6 100644
--- a/src/main/java/au/com/royalpay/payment/manage/dev/web/TestController.java
+++ b/src/main/java/au/com/royalpay/payment/manage/dev/web/TestController.java
@@ -34,6 +34,7 @@ import au.com.royalpay.payment.tools.utils.XmlFormatUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.dom4j.Element;
import org.slf4j.Logger;
@@ -245,7 +246,8 @@ public class TestController implements ApplicationEventPublisherAware {
List orders = orderMapper.listOrdersWithNoTransactions();
for (JSONObject order : orders) {
try {
- paymentApi.checkOrderStatus(order.getString("order_id"), order.getString("channel"), true);
+ String orderChannel = StringUtils.defaultIfEmpty(order.getString("order_channel"), order.getString("channel"));
+ paymentApi.checkOrderStatus(order.getString("order_id"), orderChannel, true);
} catch (Exception e) {
logger.error("fix transaction error:" + order.getString("order_id"));
}
@@ -262,7 +264,7 @@ public class TestController implements ApplicationEventPublisherAware {
if (order == null) {
throw new OrderNotExistsException();
}
- String channel = order.getString("channel");
+ String channel = StringUtils.defaultIfEmpty(order.getString("order_channel"), order.getString("channel"));
JSONObject res = new JSONObject();
switch (channel) {
case "Wechat":
@@ -286,7 +288,7 @@ public class TestController implements ApplicationEventPublisherAware {
res.put("xml", xmlStr);
break;
case "AlipayOnline":
- elem = alipayClient.checkOnlineOrderStatusByOrderId(orderId,AlipayEnvironment.getEnv().getAlipayOnlineMerchant().getPid());
+ elem = alipayClient.checkOnlineOrderStatusByOrderId(orderId, AlipayEnvironment.getEnv().getAlipayOnlineMerchant().getPid());
xmlStr = XmlFormatUtils.formatXml(elem);
res.put("xml", xmlStr);
break;
@@ -315,7 +317,7 @@ public class TestController implements ApplicationEventPublisherAware {
res.put("xml", xml);
break;
case "Alipay":
- elem = alipayClient.retailRefund(type, refundOrder.getBigDecimal("refund_exchange_rate"), refundOrder,AlipayEnvironment.getEnv().getAlipayRetailMerchant().getPid());
+ elem = alipayClient.retailRefund(type, refundOrder.getBigDecimal("refund_exchange_rate"), refundOrder, AlipayEnvironment.getEnv().getAlipayRetailMerchant().getPid());
String xmlStr = XmlFormatUtils.formatXml(elem);
res.put("xml", xmlStr);
break;
@@ -330,7 +332,7 @@ public class TestController implements ApplicationEventPublisherAware {
res.put("xml", xmlStr);
break;
case "AlipayOnline":
- elem = alipayClient.onlineRefund(refundOrder,AlipayEnvironment.getEnv().getAlipayOnlineMerchant().getPid(), type == TradeType.GATEWAY_H5);
+ elem = alipayClient.onlineRefund(refundOrder, AlipayEnvironment.getEnv().getAlipayOnlineMerchant().getPid(), type == TradeType.GATEWAY_H5);
xmlStr = XmlFormatUtils.formatXml(elem);
res.put("xml", xmlStr);
break;
@@ -389,12 +391,12 @@ public class TestController implements ApplicationEventPublisherAware {
@ManagerMapping(value = "/aliforexcel", method = RequestMethod.GET, role = ManagerRole.DEVELOPER)
public void aliforexcel(@ModelAttribute(CommonConsts.MANAGER_STATUS) JSONObject manager, HttpServletResponse httpResponse, AliExcel query) throws Exception {
- aliforexcelService.listClients(httpResponse,manager,query);
+ aliforexcelService.listClients(httpResponse, manager, query);
}
@ManagerMapping(value = "/hfClearAmount", method = RequestMethod.GET, role = ManagerRole.DEVELOPER)
public JSONObject hfUpdate(@RequestParam String datefrom, @RequestParam String dateto) {
- return hfClearAmountService.hfjsonobject(dateto,datefrom);
+ return hfClearAmountService.hfjsonobject(dateto, datefrom);
}
@ManagerMapping(value = "/hfUpdate", method = RequestMethod.GET, role = ManagerRole.DEVELOPER)
diff --git a/src/main/java/au/com/royalpay/payment/manage/mappers/payment/TransactionMapper.java b/src/main/java/au/com/royalpay/payment/manage/mappers/payment/TransactionMapper.java
index 8cebfdf09..d2b2a2dac 100644
--- a/src/main/java/au/com/royalpay/payment/manage/mappers/payment/TransactionMapper.java
+++ b/src/main/java/au/com/royalpay/payment/manage/mappers/payment/TransactionMapper.java
@@ -8,6 +8,7 @@ import com.alibaba.fastjson.JSONObject;
import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
import com.github.miemiedev.mybatis.paginator.domain.PageList;
import org.apache.ibatis.annotations.Param;
+import org.joda.time.DateTime;
import java.math.BigDecimal;
import java.util.Date;
@@ -142,4 +143,6 @@ public interface TransactionMapper {
void removeSettleRemark(@Param("clearing_id") int clearingId);
List getHfClearAmount(JSONObject params);
+
+ List analysisForATOReport(@Param("clientId") int clientId, @Param("from") Date startOfMon, @Param("to") Date endOfMon);
}
diff --git a/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientMapper.java b/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientMapper.java
index 281ef65c3..da095448a 100644
--- a/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientMapper.java
+++ b/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientMapper.java
@@ -83,6 +83,8 @@ public interface ClientMapper {
List listBySubMerchantId(@Param("sub_merchant_id") String sub_merchant_id);
+ List listClientsWithTransactionsSettled(@Param("from") Date fromDate, @Param("to") Date toDate);
+
@AutoSql(type = SqlType.SELECT)
List AllClients();
diff --git a/src/main/resources/au/com/royalpay/payment/manage/mappers/payment/TransactionMapper.xml b/src/main/resources/au/com/royalpay/payment/manage/mappers/payment/TransactionMapper.xml
index cefdfcfad..7886a2c6a 100644
--- a/src/main/resources/au/com/royalpay/payment/manage/mappers/payment/TransactionMapper.xml
+++ b/src/main/resources/au/com/royalpay/payment/manage/mappers/payment/TransactionMapper.xml
@@ -101,13 +101,17 @@
-