diff --git a/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/CleanService.java b/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/CleanService.java index c25558ab4..6ee1221d2 100644 --- a/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/CleanService.java +++ b/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/CleanService.java @@ -107,4 +107,6 @@ public interface CleanService { ByteArrayResource downloadBatchSettleReportXlsx(int batchId); JSONObject findSettleLog(int clearingId); + + JSONObject findClearingDetail(int clearingId, String clientMoniker); } diff --git a/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/impl/CleanServiceImpl.java b/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/impl/CleanServiceImpl.java index 90c1afcf9..807508477 100644 --- a/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/impl/CleanServiceImpl.java +++ b/src/main/java/au/com/royalpay/payment/manage/management/clearing/core/impl/CleanServiceImpl.java @@ -84,6 +84,7 @@ import java.text.NumberFormat; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -236,7 +237,6 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider total.put("tax_amount", 0); total.put("charge_cashback", 0); - List details = new ArrayList<>(); for (JSONObject log : logs) { addBigDecimal(total, log, "total_credit"); addBigDecimal(total, log, "total_debit"); @@ -249,22 +249,38 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider addBigDecimal(total, log, "tax_amount"); int clearingId = log.getIntValue("clearing_id"); List logDetails = clearingDetailMapper.listReportsOfSettlement(clearingId); - details.addAll(logDetails); List banks = logDetails.stream().map(detail -> detail.getString("settle_bank")).distinct().collect(Collectors.toList()); List bankStatistics = banks.stream().map(bank -> { JSONObject data = new JSONObject(); data.put("bank", bank); - data.put("total_settle", logDetails.stream() + List bankCleanLogs = logDetails.stream() .filter(detail -> bank.equals(detail.getString("settle_bank"))) + .collect(Collectors.toList()); + data.put("total_settle", bankCleanLogs.stream() .map(detail -> detail.getBigDecimal("clearing_amount")) .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO) ); - data.put("clients", logDetails.stream() - .filter(detail -> bank.equals(detail.getString("settle_bank"))) - .count()); + data.put("clients", bankCleanLogs.size()); return data; }).collect(Collectors.toList()); log.put("bank_statistics", bankStatistics); + Map cleanDaysStatistics = Stream.of(1, 2, 3).map(cleanDays -> { + JSONObject data = new JSONObject(); + List cleanDaysDetails = logDetails.stream() + .filter(detail -> detail.getIntValue("clear_days") == cleanDays) + .collect(Collectors.toList()); + data.put("clean_days", cleanDays); + data.put("total_settle", cleanDaysDetails.stream() + .map(detail -> detail.getBigDecimal("clearing_amount")) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO)); + data.put("clients", cleanDaysDetails.size()); + data.put("reports", cleanDaysDetails.size() < 20 ? cleanDaysDetails : cleanDaysDetails.subList(0, 20)); + return data; + }).collect(HashMap::new, (map, data) -> map.put("T+" + data.getIntValue("clean_days"), data), Map::putAll); + + log.put("clean_days", cleanDaysStatistics); if (StringUtils.isNotEmpty(log.getString("plan_detail"))) { try { log.put("plan_detail", JSON.parseObject(log.getString("plan_detail"))); @@ -274,8 +290,15 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider log.put("editable", DateUtils.isSameDay(log.getDate("settle_date"), new Date()) && log.getBooleanValue("editable")); log.put("channel_analysis", clearingDetailAnalysisMapper.analysisChannelReport(clearingId)); } + Map totalCleanDaysSatistics = Stream.of(1, 2, 3).map(cleanDays -> { + JSONObject cleanDaysReport = new JSONObject(); + cleanDaysReport.put("clean_days", "T+" + cleanDays); + cleanDaysReport.put("clients", logs.stream().mapToInt(log -> log.getJSONObject("clean_days").getJSONObject("T+" + cleanDays).getIntValue("clients")).sum()); + cleanDaysReport.put("amount", logs.stream().map(log -> log.getJSONObject("clean_days").getJSONObject("T+" + cleanDays).getBigDecimal("total_settle")).reduce(BigDecimal::add).orElse(BigDecimal.ZERO)); + return cleanDaysReport; + }).collect(HashMap::new, (map, report) -> map.put(report.getString("clean_days"), report), Map::putAll); total.put("logs", logs); - total.put("details", details); + total.put("clean_days", totalCleanDaysSatistics); List channels = clearingDetailAnalysisMapper.analysisChannelReportDaily(settleDate); total.put("channel_analysis", channels); return total; @@ -1048,7 +1071,7 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider Sheet sheet = wb.createSheet("Merchant_Settlement_Info_" + DateFormatUtils.format(reportDate, "yyyyMMdd")); int rowNum = 0; Row row = sheet.createRow(rowNum); - String[] title = {"process date","description","order Id", "Client Order Id", "Transaction Time", "Channel", "Gateway", "Exchange Rate", "Transaction Type", "Currency", + String[] title = {"process date", "description", "order Id", "Client Order Id", "Transaction Time", "Channel", "Gateway", "Exchange Rate", "Transaction Type", "Currency", "Input Amount", "Total Amount", "Clearing Amount(AUD)", "Sruchange Rate", "Surcharge(AUD)", "GST(AUD)", "Settle Amount(AUD)", "Remark", "Dev No", "Dev Remark"}; String[] analysis = {"Total Credit(AUD)", "Total Debit(AUD)", "Gross Amount(AUD)", "Total GST(AUD)", "Total Charge(AUD)", "Net Amount(AUD)"}; for (int i = 0; i < title.length; i++) { @@ -1656,6 +1679,13 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider return clearing; } + @Override + public JSONObject findClearingDetail(int clearingId, String clientMoniker) { + List reports = clearingDetailMapper.listReportsOfSettlement(clearingId); + return reports.stream().filter(report -> report.getString("client_moniker").equals(clientMoniker)).findFirst() + .orElseThrow(() -> new NotFoundException("No clearing log found for " + clientMoniker)); + } + private void releaseDistributedSurcharge(JSONObject clearingDetail) { int clientId = clearingDetail.getIntValue("client_id"); BigDecimal distributedSurcharge = clearingDetail.getBigDecimal("distributed_surcharge"); diff --git a/src/main/java/au/com/royalpay/payment/manage/management/clearing/web/SettlementDevController.java b/src/main/java/au/com/royalpay/payment/manage/management/clearing/web/SettlementDevController.java index 288da4cf8..a0124f00c 100644 --- a/src/main/java/au/com/royalpay/payment/manage/management/clearing/web/SettlementDevController.java +++ b/src/main/java/au/com/royalpay/payment/manage/management/clearing/web/SettlementDevController.java @@ -10,7 +10,6 @@ import au.com.royalpay.payment.tools.merchants.beans.BalanceGroup; import au.com.royalpay.payment.tools.permission.enums.ManagerRole; import com.alibaba.fastjson.JSONObject; import org.springframework.core.io.ByteArrayResource; -import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -68,6 +67,11 @@ public class SettlementDevController { return res; } + @GetMapping("/clearing/{clearingId}/client_detail/{clientMoniker}") + public JSONObject getClientSettleDetail(@PathVariable int clearingId, @PathVariable String clientMoniker) { + return cleanService.findClearingDetail(clearingId, clientMoniker); + } + @PutMapping("/reports/{date}/clearings/{clearingId}/bank_distribution") public void modifyClearingBank(@PathVariable String date, @PathVariable int clearingId, @RequestBody JSONObject bankDistribution) { try { diff --git a/src/main/ui/static/analysis/clearing-log.js b/src/main/ui/static/analysis/clearing-log.js index 959b815df..f0a957fb8 100644 --- a/src/main/ui/static/analysis/clearing-log.js +++ b/src/main/ui/static/analysis/clearing-log.js @@ -1,7 +1,7 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], function (angular, Decimal) { 'use strict'; - var colors = ['#00c0ef', '#00a65a', '#ff851b', '#f39c12', '#d81b60', '#605ca8', '#dd4b39', '#008080', '#8B008B', '#D2691E', '#708090']; - var app = angular.module('clearingLogs', ['ui.bootstrap', 'ui.router', 'ngEcharts']); + let colors = ['#00c0ef', '#00a65a', '#ff851b', '#f39c12', '#d81b60', '#605ca8', '#dd4b39', '#008080', '#8B008B', '#D2691E', '#708090']; + let app = angular.module('clearingLogs', ['ui.bootstrap', 'ui.router', 'ngEcharts']); app.config(['$stateProvider', function ($stateProvider) { $stateProvider.state('clearingLogs', { url: '/analysis/clearing_logs', @@ -54,7 +54,7 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func app.controller('clearingLogsCtrl', ['$scope', '$http', '$filter', '$timeout', '$uibModal', 'commonDialog', 'chartParser', function ($scope, $http, $filter, $timeout, $uibModal, commonDialog, chartParser) { $scope.loadMonthLog = function (month) { - var monthStr = $filter('date')(month, 'yyyyMM'); + let monthStr = $filter('date')(month, 'yyyyMM'); $http.get('/sys/settlement/month/' + monthStr + '/settled_dates').then(function (resp) { $scope.settledDates = resp.data; }); @@ -68,39 +68,39 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func $scope.loadClearingLogs(1); }; $scope.chooseYesterday = function () { - var yesterday = new Date(); + let yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); $scope.params.begin = $scope.params.end = yesterday; $scope.loadClearingLogs(1); }; $scope.chooseLast7Days = function () { $scope.params.end = new Date(); - var day = new Date(); + let day = new Date(); day.setDate(day.getDate() - 7); $scope.params.begin = day; $scope.loadClearingLogs(1); }; $scope.thisMonth = function () { $scope.params.end = new Date(); - var monthBegin = new Date(); + let monthBegin = new Date(); monthBegin.setDate(1); $scope.params.begin = monthBegin; $scope.loadClearingLogs(1); }; $scope.lastMonth = function () { - var monthFinish = new Date(); + let monthFinish = new Date(); monthFinish.setDate(0); $scope.params.end = monthFinish; - var monthBegin = new Date(); + let monthBegin = new Date(); monthBegin.setDate(0); monthBegin.setDate(1); $scope.params.begin = monthBegin; $scope.loadClearingLogs(1); }; $scope.thisYear = function () { - var yearFinish = new Date(); + let yearFinish = new Date(); $scope.params.end = yearFinish; - var currentYearFirstDate = new Date(new Date().getFullYear(), 0, 1); + let currentYearFirstDate = new Date(new Date().getFullYear(), 0, 1); $scope.params.begin = currentYearFirstDate; $scope.loadClearingLogs(1); }; @@ -124,7 +124,7 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func }; $scope.loadClearingLogs = function (page) { - var params = angular.copy($scope.params); + let params = angular.copy($scope.params); params.page = page || $scope.pagination.page || 1; if (params.begin) { params.begin = $filter('date')(params.begin, 'yyyyMMdd'); @@ -140,15 +140,15 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func }); analysisLog(params); }; - var analysisLog = function (params) { + let analysisLog = function (params) { $http.get('/sys/clean_logs/analysis', {params: params}).then(function (resp) { $scope.analysis = resp.data; }) }; $scope.loadClearingLogsHistory = function (days) { - var endDate = new Date(); - var startDate = new Date(); + let endDate = new Date(); + let startDate = new Date(); startDate.setDate(startDate.getDate() - days); $http.get('/sys/clean_logs/logs', { params: { @@ -160,7 +160,7 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func }) }; $scope.loadClearingLogsHistory(7); - var clearingHistoryConfig = { + let clearingHistoryConfig = { chart: { tooltip: { trigger: 'axis', @@ -224,7 +224,7 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func $scope.generateRate = function (client) { commonDialog.confirm({title: 'Confirm Required', content: '根据上季度总交易额自动计算下周期费率,确定执行?'}).then(function () { $http.put('/manage/clearing/clients/' + client.client_moniker + '/auto_rate').then(function (resp) { - var content = 'New Rate value=' + resp.data.rate_value + '; expiry_time=' + $filter('date')(resp.data.expiry_time, 'dd/MMM/yyyy'); + let content = 'New Rate value=' + resp.data.rate_value + '; expiry_time=' + $filter('date')(resp.data.expiry_time, 'dd/MMM/yyyy'); commonDialog.alert({ type: 'success', title: 'New Rate Generated', @@ -248,77 +248,28 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func $scope.analysisFilter = {}; $scope.currentAnalysis = $scope.detail; $scope.pageCtrl = {visible: {}}; + $scope.searchingClient = null; - function getAnalysisTemplate() { - return [ - {settleDays: 1, clients: 0, settleAmount: 0, settles: []}, - {settleDays: 2, clients: 0, settleAmount: 0, settles: []}, - {settleDays: 3, clients: 0, settleAmount: 0, settles: []} - ]; - } - - $scope.reloadPage = function () { - $state.reload(); - }; - - $scope.settleAnalysis = getAnalysisTemplate(); + $scope.search = {client_moniker: null}; - $scope.batchAnalysis = { - 'All': $scope.settleAnalysis - }; - angular.forEach($scope.detail.logs, function (batch) { - $scope.batchAnalysis[batch.clearing_id + ''] = getAnalysisTemplate(); - }); - - $scope.endIndexMap = {}; - $scope.initEndIndex = 20; - - $scope.more = function (key) { - - var endIndex = $scope.endIndexMap[key] + $scope.initEndIndex; - $scope.endIndexMap[key] = endIndex; - if (endIndex > $scope.clientsMap[key].clients) { - $scope.endIndexMap[key] = $scope.clientsMap[key].clients; + $scope.searchClient = function () { + if (!$scope.search.client_moniker) { + $scope.searchingClient = null; + } + if ($scope.currentAnalysis.clearing_id) { + $http.get('/sys/settlement/clearing/' + $scope.currentAnalysis.clearing_id + '/client_detail/' + $scope.search.client_moniker).then(function (resp) { + $scope.searchingClient = resp.data; + }, function (resp) { + commonDialog.alert({type: 'error', title: 'Error', content: resp.data.message}); + }) } - - }; - - $scope.packup = function (key) { - $scope.endIndexMap[key] = $scope.initEndIndex; - var length = $scope.clientsMap[key].clients; - if (length <= $scope.initEndIndex) - $scope.endIndexMap[key] = length; }; - $scope.displayAll = function (key) { - var length = $scope.clientsMap[key].clients; - $scope.endIndexMap[key] = length; + $scope.reloadPage = function () { + $state.reload(); }; - - angular.forEach($scope.detail.details, function (settleItem) { - var settleDays = settleItem.clear_days; - attachAnalysis($scope.settleAnalysis[Math.min(settleDays - 1, 2)]); - attachAnalysis($scope.batchAnalysis[settleItem.clearing_id + ''][Math.min(settleDays - 1, 2)]); - - function attachAnalysis(analysisItem) { - analysisItem.settles.push(settleItem); - analysisItem.clients++; - analysisItem.settleAmount = Decimal.add(analysisItem.settleAmount, settleItem.clearing_amount).toFixed(2, Decimal.ROUND_FLOOR); - } - }); - - $scope.clientsMap = $scope.settleAnalysis; - - for (var key in $scope.clientsMap) { - $scope.endIndexMap[key] = $scope.initEndIndex; - var length = $scope.clientsMap[key].clients; - if (length <= $scope.initEndIndex) - $scope.endIndexMap[key] = length; - } - - - var nowStr = $filter('date')(new Date(), "yyyy-MM-dd"); + let nowStr = $filter('date')(new Date(), "yyyy-MM-dd"); $scope.datePattern = $stateParams.date; if ($scope.datePattern == nowStr) { $scope.sendNotice = true; @@ -336,6 +287,8 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func }; $scope.switchSettleBatch = function (batch) { + $scope.search.client_moniker = null; + $scope.searchingClient = null; if (batch == null) { $scope.currentAnalysis = $scope.detail; $scope.analysisFilter.clearing_id = null; @@ -408,10 +361,11 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func }; $scope.rollbackSettlement = function () { - var log = $scope.getCurrentLog(); - commonDialog.confirm({ + let log = $scope.getCurrentLog(); + commonDialog.dangerConfirm({ title: '确认操作', - content: '回滚当前清算,id=' + log.clearing_id + ',确认?' + operation: '回滚当前清算,id=' + log.clearing_id + ',确认?', + content: 'Undo Settlement' }).then(function () { $http.delete('/sys/settlement/reports/' + $stateParams.date + '/clearings/' + log.clearing_id).then(function () { $state.reload(); @@ -436,7 +390,7 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func } }); $scope.remainingAmount = function () { - var total = clearingBatch.net_amount; + let total = clearingBatch.net_amount; angular.forEach($scope.bankData, function (config) { total = Decimal.sub(total, config.amount); }); @@ -444,7 +398,7 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func }; $scope.submitDistribution = function () { $scope.errmsg = null; - var data = {}; + let data = {}; angular.forEach($scope.bankData, function (config) { data[config.bank] = config.amount; }); diff --git a/src/main/ui/static/analysis/templates/settlement_detail.html b/src/main/ui/static/analysis/templates/settlement_detail.html index d6036335d..703a0e606 100644 --- a/src/main/ui/static/analysis/templates/settlement_detail.html +++ b/src/main/ui/static/analysis/templates/settlement_detail.html @@ -81,13 +81,13 @@ -
+
  • Bank Statistics
  • -
  • +
  • @@ -181,15 +181,73 @@
+
+
+
+
+ +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Client MonikerTransaction DateGross AmountRateTotal ChargeNet AmountTax AmountSettle DaysTransactions
+ + + + {{searchingClient.settle_date_from|limitTo:10}} + ~ + {{searchingClient.settle_date_to|limitTo:10}} + + + + +
+
+
+
-
- T+{{group.settleDays}}: Clients:{{group.clients}} - Amount:{{group.settleAmount|currency:''}} - Click to show - Click to hide + ng-repeat=" (key, group) in currentAnalysis.clean_days"> +
+ {{group.clean_days}}: Clients:{{group.clients}} + Amount:{{group.total_settle|currency:''}} + Click to show + Click to hide
-
+
@@ -205,9 +263,10 @@ - +
- + @@ -229,23 +288,6 @@
- - - 更多 - - - - | - - - 全部 - - - 收起 - -
diff --git a/src/main/ui/static/commons/services/commonDialog.js b/src/main/ui/static/commons/services/commonDialog.js index 6227d7e6b..a089a7bf2 100644 --- a/src/main/ui/static/commons/services/commonDialog.js +++ b/src/main/ui/static/commons/services/commonDialog.js @@ -4,7 +4,7 @@ */ define(['../app', 'angular'], function (app, angular) { 'use strict'; - var defaultCfg = { + let defaultCfg = { size: null, backdrop: true, type: 'default' @@ -12,8 +12,8 @@ define(['../app', 'angular'], function (app, angular) { app.factory('commonDialog', ['$q', '$uibModal', function ($q, $uibModal) { return { alert: function (cfg) { - var choises = [{label: 'OK', className: 'btn-default', key: '1'}]; - var config = { + let choises = [{label: 'OK', className: 'btn-default', key: '1'}]; + let config = { title: cfg.title, content: cfg.content, contentHtml: cfg.contentHtml, @@ -22,7 +22,7 @@ define(['../app', 'angular'], function (app, angular) { choises: choises, type: cfg.type }; - var defer = $q.defer(); + let defer = $q.defer(); showModalDialog(config).then(function () { defer.resolve(); }, function () { @@ -31,18 +31,18 @@ define(['../app', 'angular'], function (app, angular) { return defer.promise; }, confirm: function (cfg) { - var choises = [{label: 'OK', className: 'btn-success', key: '1'}, + let choises = [{label: 'OK', className: 'btn-success', key: '1'}, {label: 'Cancel', className: 'btn-danger', key: '2', dismiss: true}]; - var config = { + let config = { title: cfg.title, content: cfg.content, contentHtml: cfg.contentHtml, - json:cfg.json, + json: cfg.json, backdrop: cfg.backdrop, size: cfg.size, choises: cfg.choises || choises }; - var deferred = $q.defer(); + let deferred = $q.defer(); showModalDialog(config).then(function (choice) { if (choice.dismiss) { deferred.reject(); @@ -54,6 +54,29 @@ define(['../app', 'angular'], function (app, angular) { }); return deferred.promise; }, + dangerConfirm: function (cfg) { + return $uibModal.open({ + backdrop: 'static', keyboard: false, + templateUrl: '/static/commons/templates/danger_confirm.html', + size: cfg.size || 'sm', + resolve: { + cfg: function () { + return cfg; + } + }, + controller: ['$scope', 'cfg', function ($scope, cfg) { + $scope.title = cfg.title || 'Confirm Operation'; + $scope.operation = cfg.operation; + $scope.content = cfg.content || 'Confirm Operation'; + $scope.data = {}; + $scope.submit = function () { + if ($scope.content === $scope.data.text) { + $scope.$close(); + } + }; + }] + }).result; + }, inputNum: function (cfg) { return $uibModal.open({ templateUrl: '/static/commons/templates/num_input.html', @@ -109,12 +132,12 @@ define(['../app', 'angular'], function (app, angular) { } }]); app.controller('commonModalCtrl', ['$scope', '$timeout', 'cfg', function ($scope, $timeout, cfg) { - var bgClasses = { + let bgClasses = { success: 'bg-success', error: 'bg-warning', info: 'bg-info' }; - var glyIcons = { + let glyIcons = { success: 'glyphicon-ok-circle', error: 'glyphicon-remove-circle', info: 'glyphicon-info-sign' diff --git a/src/main/ui/static/commons/templates/danger_confirm.html b/src/main/ui/static/commons/templates/danger_confirm.html new file mode 100644 index 000000000..7185a0697 --- /dev/null +++ b/src/main/ui/static/commons/templates/danger_confirm.html @@ -0,0 +1,25 @@ + + + \ No newline at end of file