From 4d6ff46b7832a13d35b3c582d784c6d4e7f3c04d Mon Sep 17 00:00:00 2001 From: luoyang Date: Fri, 20 Sep 2019 14:52:08 +0800 Subject: [PATCH] =?UTF-8?q?add=20app=E7=88=B6=E5=95=86=E6=88=B7=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E7=AE=A1=E7=90=86=E3=80=81=E9=A3=8E=E6=8E=A7=E4=B8=9A?= =?UTF-8?q?=E5=8A=A1=E5=A2=9E=E5=8A=A0remark?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 +- .../manage/appclient/beans/AppClientBean.java | 70 +++- .../appclient/beans/AppPaymentConfigBean.java | 56 ++++ .../appclient/core/RetailAppService.java | 17 +- .../core/impls/RetailAppServiceImp.java | 300 +++++++++++++----- .../appclient/web/RetailAppController.java | 36 ++- .../core/impls/ClientManagerImpl.java | 11 +- .../merchants/web/PartnerApplyController.java | 6 - .../core/RiskBusinessService.java | 2 + .../core/impl/RiskBusinessServiceImpl.java | 7 + .../web/RiskBusinessController.java | 6 + .../core/impls/SignInAccountServiceImpl.java | 106 +++---- src/main/ui/static/analysis/risk_business.js | 12 +- .../analysis/templates/riskEvent_detail.html | 43 +++ 14 files changed, 538 insertions(+), 141 deletions(-) create mode 100644 src/main/java/au/com/royalpay/payment/manage/appclient/beans/AppPaymentConfigBean.java diff --git a/pom.xml b/pom.xml index e4a09fb22..89d3aee53 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 4.0.0 manage - 1.2.14 + 1.2.15 UTF-8 @@ -78,6 +78,11 @@ + + com.alibaba + fastjson + 1.2.58 + org.springframework.boot spring-boot-starter-websocket diff --git a/src/main/java/au/com/royalpay/payment/manage/appclient/beans/AppClientBean.java b/src/main/java/au/com/royalpay/payment/manage/appclient/beans/AppClientBean.java index 5e33057b7..202b94140 100644 --- a/src/main/java/au/com/royalpay/payment/manage/appclient/beans/AppClientBean.java +++ b/src/main/java/au/com/royalpay/payment/manage/appclient/beans/AppClientBean.java @@ -24,7 +24,16 @@ public class AppClientBean { private String suburb; private String postcode; private String state; - + @JSONField(name = "legal_person") + private String legalPerson; + @JSONField(name = "legal_phone") + private String legalPhone; + @JSONField(name = "legal_email") + private String legalEmail; + @JSONField(name = "legal_job_title") + private String legalJobTitle; + @JSONField(name = "contact_job") + private String contactJob; public JSONObject updateObject() { JSONObject res = new JSONObject(); @@ -55,7 +64,26 @@ public class AppClientBean { if (state != null) { res.put("state", state); } + if (contactJob != null) { + res.put("contact_job", contactJob); + } + return res; + } + public JSONObject legalObject() { + JSONObject res = new JSONObject(); + if (legalPerson != null) { + res.put("legal_person", legalPerson); + } + if (legalPhone != null) { + res.put("legal_phone", legalPhone); + } + if (legalEmail != null) { + res.put("legal_email", legalEmail); + } + if (legalJobTitle != null) { + res.put("legal_job_title", legalJobTitle); + } return res; } @@ -137,4 +165,44 @@ public class AppClientBean { public void setState(String state) { this.state = state; } + + public String getContactJob() { + return contactJob; + } + + public String getLegalEmail() { + return legalEmail; + } + + public String getLegalJobTitle() { + return legalJobTitle; + } + + public String getLegalPerson() { + return legalPerson; + } + + public String getLegalPhone() { + return legalPhone; + } + + public void setContactJob(String contactJob) { + this.contactJob = contactJob; + } + + public void setLegalEmail(String legalEmail) { + this.legalEmail = legalEmail; + } + + public void setLegalJobTitle(String legalJobTitle) { + this.legalJobTitle = legalJobTitle; + } + + public void setLegalPerson(String legalPerson) { + this.legalPerson = legalPerson; + } + + public void setLegalPhone(String legalPhone) { + this.legalPhone = legalPhone; + } } diff --git a/src/main/java/au/com/royalpay/payment/manage/appclient/beans/AppPaymentConfigBean.java b/src/main/java/au/com/royalpay/payment/manage/appclient/beans/AppPaymentConfigBean.java new file mode 100644 index 000000000..2be37bb50 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/appclient/beans/AppPaymentConfigBean.java @@ -0,0 +1,56 @@ +package au.com.royalpay.payment.manage.appclient.beans; + +import com.alibaba.fastjson.annotation.JSONField; + +public class AppPaymentConfigBean { + @JSONField(name="api_surcharge") + private Boolean apiSurcharge; + @JSONField(name="retail_surcharge") + private Boolean retailSurcharge; + @JSONField(name="qrcode_surcharge") + private Boolean qrcodeSurcharge; + @JSONField(name = "require_remark") + private Boolean requireRemark; + @JSONField(name = "require_custinfo") + private Boolean requireCustinfo; + + public Boolean getApiSurcharge() { + return apiSurcharge; + } + + public void setApiSurcharge(Boolean apiSurcharge) { + this.apiSurcharge = apiSurcharge; + } + + public Boolean getRetailSurcharge() { + return retailSurcharge; + } + + public void setRetailSurcharge(Boolean retailSurcharge) { + this.retailSurcharge = retailSurcharge; + } + + public Boolean getQrcodeSurcharge() { + return qrcodeSurcharge; + } + + public void setQrcodeSurcharge(Boolean qrcodeSurcharge) { + this.qrcodeSurcharge = qrcodeSurcharge; + } + + public Boolean getRequireCustinfo() { + return requireCustinfo; + } + + public Boolean getRequireRemark() { + return requireRemark; + } + + public void setRequireCustinfo(Boolean requireCustinfo) { + this.requireCustinfo = requireCustinfo; + } + + public void setRequireRemark(Boolean requireRemark) { + this.requireRemark = requireRemark; + } +} diff --git a/src/main/java/au/com/royalpay/payment/manage/appclient/core/RetailAppService.java b/src/main/java/au/com/royalpay/payment/manage/appclient/core/RetailAppService.java index 0191856da..efab02aaa 100644 --- a/src/main/java/au/com/royalpay/payment/manage/appclient/core/RetailAppService.java +++ b/src/main/java/au/com/royalpay/payment/manage/appclient/core/RetailAppService.java @@ -1,6 +1,7 @@ package au.com.royalpay.payment.manage.appclient.core; import au.com.royalpay.payment.manage.appclient.beans.AppClientBean; +import au.com.royalpay.payment.manage.appclient.beans.AppPaymentConfigBean; import au.com.royalpay.payment.manage.appclient.beans.AppQueryBean; import au.com.royalpay.payment.manage.merchants.beans.ClientAuthFilesInfo; import au.com.royalpay.payment.manage.merchants.beans.ClientUpdateInfo; @@ -76,6 +77,8 @@ public interface RetailAppService { JSONObject getClientCurrentRateNew(JSONObject device); + JSONObject getClientCurrentRateNewByMoniker(JSONObject device, String clientMoniker); + JSONObject listDailyTransactions(String dateStr, String timezone, boolean thisDevOnly, JSONObject device,String app_client_ids); JSONObject getActivities(JSONObject device, String activity_page, int page, int limit); @@ -90,10 +93,16 @@ public interface RetailAppService { void updateClient(JSONObject device, AppClientBean appClientBean); + void updateClientByMoniker(JSONObject device, String clientMoniker, AppClientBean appClientBean); + JSONObject getClientInfo(JSONObject device); + JSONObject getClientInfoByMoniker(JSONObject device, String clientMoniker); + JSONObject getClientInfoRealtime(JSONObject device); + JSONObject getQRCodePaySurChargeByMoniker(JSONObject device, String clientMoniker); + JSONObject getClientInfoMe(JSONObject device); JSONObject updateRetailConfig(JSONObject device, boolean paySurcharge); @@ -136,7 +145,9 @@ public interface RetailAppService { JSONObject getQrcode(JSONObject device, QRCodeConfig config,int client_id); - void changeSurchargeEnable(JSONObject device, UpdateSurchargeDTO updateSurchargeDTO); + void changeSurchargeEnable(JSONObject device, AppPaymentConfigBean appPaymentConfigBean); + + void changePaymentConfigByMoniker(JSONObject device, String clientMoniker, AppPaymentConfigBean appPaymentConfigBean); JSONObject getInvoiceData(JSONObject device, AppQueryBean appQueryBean) throws Exception; @@ -146,6 +157,8 @@ public interface RetailAppService { JSONObject getAuthFiles(JSONObject device); + JSONObject getAuthFilesByMoniker(JSONObject device, String clientMoniker); + void uploadAuthFiles(JSONObject device,ClientAuthFilesInfo clientAuthFilesInfo); void updateMerchantInfo(JSONObject device, ClientUpdateInfo clientUpdateInfo); @@ -209,4 +222,6 @@ public interface RetailAppService { void deleteGreenChannelAuthFiles(JSONObject device, String filesInfo); void commitAuthFilesToCompliance(JSONObject device,JSONObject photoInfo); + + boolean isSubPartner(JSONObject device, String clientMoniker); } diff --git a/src/main/java/au/com/royalpay/payment/manage/appclient/core/impls/RetailAppServiceImp.java b/src/main/java/au/com/royalpay/payment/manage/appclient/core/impls/RetailAppServiceImp.java index e59b57858..6c5dc554f 100644 --- a/src/main/java/au/com/royalpay/payment/manage/appclient/core/impls/RetailAppServiceImp.java +++ b/src/main/java/au/com/royalpay/payment/manage/appclient/core/impls/RetailAppServiceImp.java @@ -6,6 +6,7 @@ import au.com.royalpay.payment.manage.activities.app_index.core.AppActService; import au.com.royalpay.payment.manage.analysis.mappers.CustomerAndOrdersStatisticsMapper; import au.com.royalpay.payment.manage.analysis.mappers.TransactionAnalysisMapper; import au.com.royalpay.payment.manage.appclient.beans.AppClientBean; +import au.com.royalpay.payment.manage.appclient.beans.AppPaymentConfigBean; import au.com.royalpay.payment.manage.appclient.beans.AppQueryBean; import au.com.royalpay.payment.manage.appclient.core.RetailAppService; import au.com.royalpay.payment.manage.cashback.core.CashbackService; @@ -93,6 +94,7 @@ import org.thymeleaf.spring5.SpringTemplateEngine; import java.awt.image.BufferedImage; import java.io.*; +import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DateFormat; @@ -110,6 +112,7 @@ import java.util.TimeZone; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import javax.annotation.Resource; import javax.servlet.ServletOutputStream; @@ -352,7 +355,7 @@ public class RetailAppServiceImp implements RetailAppService { } @Override - public void changeSurchargeEnable(JSONObject device, UpdateSurchargeDTO updateSurchargeDTO) { + public void changeSurchargeEnable(JSONObject device, AppPaymentConfigBean appPaymentConfigBean) { JSONObject client = clientManager.getClientInfo(device.getIntValue("client_id")); JSONObject account = clientAccountMapper.findById(device.getString("account_id")); if (PartnerRole.getRole(account.getIntValue("role")) != PartnerRole.ADMIN) { @@ -361,17 +364,65 @@ public class RetailAppServiceImp implements RetailAppService { if (client == null) { throw new NotFoundException("Client not found, please check"); } - if (updateSurchargeDTO.getApiSurcharge() != null) { + if (appPaymentConfigBean.getApiSurcharge() != null) { clientModifySupport.processClientConfigModify( - new SwitchPermissionModify(account, client.getString("client_moniker"), "api_surcharge", updateSurchargeDTO.getApiSurcharge())); + new SwitchPermissionModify(account, client.getString("client_moniker"), "api_surcharge", appPaymentConfigBean.getApiSurcharge())); } - if (updateSurchargeDTO.getQrcodeSurcharge() != null) { + if (appPaymentConfigBean.getQrcodeSurcharge() != null) { clientModifySupport.processClientConfigModify( - new SwitchPermissionModify(account, client.getString("client_moniker"), "qrcode_surcharge", updateSurchargeDTO.getQrcodeSurcharge())); + new SwitchPermissionModify(account, client.getString("client_moniker"), "qrcode_surcharge", appPaymentConfigBean.getQrcodeSurcharge())); } - if (updateSurchargeDTO.getRetailSurcharge() != null) { + if (appPaymentConfigBean.getRetailSurcharge() != null) { clientModifySupport.processClientConfigModify( - new SwitchPermissionModify(account, client.getString("client_moniker"), "retail_surcharge", updateSurchargeDTO.getRetailSurcharge())); + new SwitchPermissionModify(account, client.getString("client_moniker"), "retail_surcharge", appPaymentConfigBean.getRetailSurcharge())); + } + + if (appPaymentConfigBean.getRequireRemark() != null) { + clientModifySupport.processClientConfigModify( + new SwitchPermissionModify(account, client.getString("client_moniker"), "require_remark", appPaymentConfigBean.getRequireRemark())); + } + + if (appPaymentConfigBean.getRequireCustinfo() != null) { + clientModifySupport.processClientConfigModify( + new SwitchPermissionModify(account, client.getString("client_moniker"), "require_custinfo", appPaymentConfigBean.getRequireCustinfo())); + } + } + + @Override + public void changePaymentConfigByMoniker(JSONObject device, String clientMoniker, AppPaymentConfigBean appPaymentConfigBean) { + JSONObject client = clientManager.getClientInfo(device.getIntValue("client_id")); + JSONObject account = clientAccountMapper.findById(device.getString("account_id")); + if (PartnerRole.getRole(account.getIntValue("role")) != PartnerRole.ADMIN) { + throw new ForbiddenException("You have no permission"); + } + if (client == null) { + throw new NotFoundException("Client not found, please check"); + } + boolean isSubPartner = isSubPartner(device, clientMoniker); + if (!isSubPartner) { + throw new ForbiddenException("You have no permission"); + } + if (appPaymentConfigBean.getApiSurcharge() != null) { + clientModifySupport.processClientConfigModify( + new SwitchPermissionModify(account, clientMoniker, "api_surcharge", appPaymentConfigBean.getApiSurcharge())); + } + if (appPaymentConfigBean.getQrcodeSurcharge() != null) { + clientModifySupport.processClientConfigModify( + new SwitchPermissionModify(account, clientMoniker, "qrcode_surcharge", appPaymentConfigBean.getQrcodeSurcharge())); + } + if (appPaymentConfigBean.getRetailSurcharge() != null) { + clientModifySupport.processClientConfigModify( + new SwitchPermissionModify(account, clientMoniker, "retail_surcharge", appPaymentConfigBean.getRetailSurcharge())); + } + + if (appPaymentConfigBean.getRequireRemark() != null) { + clientModifySupport.processClientConfigModify( + new SwitchPermissionModify(account, clientMoniker, "require_remark", appPaymentConfigBean.getRequireRemark())); + } + + if (appPaymentConfigBean.getRequireCustinfo() != null) { + clientModifySupport.processClientConfigModify( + new SwitchPermissionModify(account, clientMoniker, "require_custinfo", appPaymentConfigBean.getRequireCustinfo())); } } @@ -429,6 +480,18 @@ public class RetailAppServiceImp implements RetailAppService { return clientManager.getAuthFiles(account, client.getString("client_moniker")); } + @Override + public JSONObject getAuthFilesByMoniker(JSONObject device, String clientMoniker) { + if (!isSubPartner(device, clientMoniker)) { + throw new ForbiddenException("You have no permission"); + } + JSONObject client = clientManager.getClientInfoByMoniker(clientMoniker); + if (client == null) { + throw new NotFoundException("Client not found, please check"); + } + return clientManager.getAllAuthFiles(null, clientMoniker); + } + @Override public void uploadAuthFiles(JSONObject device, ClientAuthFilesInfo clientAuthFilesInfo) { JSONObject client = clientManager.getClientInfo(device.getIntValue("client_id")); @@ -593,7 +656,24 @@ public class RetailAppServiceImp implements RetailAppService { throw new ForbiddenException("You have no permission"); } clientManager.updateAppClient(account, device.getIntValue("client_id"), appClientBean); + } + @Override + public void updateClientByMoniker(JSONObject device, String clientMoniker, AppClientBean appClientBean) { + String clientType = device.getString("client_type"); + deviceSupport.findRegister(clientType); + JSONObject account = clientAccountMapper.findById(device.getString("account_id")); + if (device.getIntValue("client_id") != account.getIntValue("client_id") || PartnerRole.getRole(account.getIntValue("role")) == PartnerRole.CASHIER) { + throw new ForbiddenException("You have no permission"); + } + if (!isSubPartner(device, clientMoniker)) { + throw new ForbiddenException("You have no permission"); + } + JSONObject client = clientManager.getClientInfoByMoniker(clientMoniker); + if (client == null) { + throw new NotFoundException("Client not found, please check"); + } + clientManager.updateAppClient(account, client.getIntValue("client_id"), appClientBean); } @Override @@ -602,30 +682,25 @@ public class RetailAppServiceImp implements RetailAppService { deviceSupport.findRegister(clientType); JSONObject clientWithConfig = clientMapper.findClient(device.getIntValue("client_id")); clientWithConfig.putAll(clientConfigService.find(device.getIntValue("client_id"))); - JSONObject res = SignInAccountServiceImpl.clientInfoWithNoSecretInfo(clientWithConfig); - res.put("is_skip_clearing", res.getBoolean("skip_clearing")); + JSONObject res = getClientBaseInfo(clientWithConfig); if (clientType.equals("iphone")) { res.put("skip_clearing", !res.getBoolean("skip_clearing")); } - if ("4".equals(clientWithConfig.getString("source"))) { - res.put("refuse_remark", clientWithConfig.getString("refuse_remark")); - res.put("base_info_lack", false); - res.put("compliance_info_lack", false); - if (StringUtils.isEmpty(clientWithConfig.getString("business_structure")) || StringUtils.isEmpty(clientWithConfig.getString("logo_url")) - || StringUtils.isEmpty(clientWithConfig.getString("description")) - || ("Company".equals(clientWithConfig.getString("business_structure")) && StringUtils.isEmpty(clientWithConfig.getString("acn"))) - || (!"Company".equals(clientWithConfig.getString("business_structure")) && StringUtils.isEmpty(clientWithConfig.getString("abn"))) - || (StringUtils.isEmpty(clientWithConfig.getString("company_website")) && StringUtils.isEmpty(clientWithConfig.getString("company_photo")) - && StringUtils.isEmpty(clientWithConfig.getString("store_photo")))) { - res.put("base_info_lack", true); + return res; + } - } - JSONObject file = clientManager.getAuthFiles(null, clientWithConfig.getString("client_moniker")); - for (String s : fileName) { - if (file.getString(s) == null) { - res.put("compliance_info_lack", true); - } - } + @Override + public JSONObject getClientInfoByMoniker(JSONObject device, String clientMoniker) { + String clientType = device.getString("client_type"); + deviceSupport.findRegister(clientType); + if (!isSubPartner(device, clientMoniker)) { + throw new ForbiddenException("You have no permission"); + } + JSONObject client = clientManager.getClientInfoByMoniker(clientMoniker); + client.putAll(clientConfigService.find(client.getIntValue("client_id"))); + JSONObject res = getClientBaseInfo(client); + if (clientType.equals("iphone")) { + res.put("skip_clearing", !res.getBoolean("skip_clearing")); } return res; } @@ -637,6 +712,22 @@ public class RetailAppServiceImp implements RetailAppService { return clientWithConfig; } + @Override + public JSONObject getQRCodePaySurChargeByMoniker(JSONObject device, String clientMoniker) { + if (!isSubPartner(device, clientMoniker)) { + throw new ForbiddenException("You have no permission"); + } + JSONObject client = clientManager.getClientInfoByMoniker(clientMoniker); + client.putAll(clientConfigService.find(client.getIntValue("client_id"))); + JSONObject result = new JSONObject(); + result.put("qrcode_surcharge", client.getBooleanValue("qrcode_surcharge")); + result.put("retail_surcharge", client.getBooleanValue("retail_surcharge")); + result.put("api_surcharge", client.getBooleanValue("api_surcharge")); + result.put("require_custinfo", client.getBooleanValue("require_custinfo")); + result.put("require_remark", client.getBooleanValue("require_remark")); + return result; + } + @Override public JSONObject getClientInfoMe(JSONObject device) { JSONObject result = new JSONObject(); @@ -1490,50 +1581,21 @@ public class RetailAppServiceImp implements RetailAppService { String clientType = device.getString("client_type"); deviceSupport.findRegister(clientType); int clientId = device.getIntValue("client_id"); - Date now = new Date(); - JSONObject res = merchantInfoProvider.clientCurrentRate(clientId, now, "Wechat"); - JSONObject client = clientManager.getClientInfo(clientId); - Assert.notNull(client, "Client is null"); - JSONObject clientConfig = clientConfigService.find(clientId); - ArrayList channels = new ArrayList<>(); - if (clientConfig.getBigDecimal("customer_surcharge_rate") != null) { - res.put("customer_surcharge_rate", clientConfig.getBigDecimal("customer_surcharge_rate")); - } - res.put("max_customer_surcharge_rate", PlatformEnvironment.getEnv().getMaxCustomerSurchargeRate()); - JSONObject wechat = getChannel(clientId, now, "Wechat"); - if (wechat.containsKey("channel")) { - channels.add(wechat); - } - JSONObject alipay = getChannel(clientId, now, "Alipay"); - if (alipay.containsKey("channel")) { - channels.add(alipay); - } - JSONObject bestpay = getChannel(clientId, now, "Bestpay"); - if (bestpay.containsKey("channel")) { - channels.add(bestpay); - } - JSONObject jd = getChannel(clientId, now, "jd"); - if (jd.containsKey("channel")) { - channels.add(jd); - } - JSONObject AlipayOnline = getChannel(clientId, now, "AlipayOnline"); - if (AlipayOnline.containsKey("channel")) { - channels.add(AlipayOnline); + return getClientRateByClientId(clientId); + } + + @Override + public JSONObject getClientCurrentRateNewByMoniker(JSONObject device, String clientMoniker) { + String clientType = device.getString("client_type"); + deviceSupport.findRegister(clientType); + if (!isSubPartner(device, clientMoniker)) { + throw new ForbiddenException("You have no permission"); } -// JSONObject Hf = getChannel(clientId, now, "hf"); -// if (Hf.containsKey("channel")) { -// channels.add(Hf); -// } -// JSONObject Yeepay = getChannel(clientId, now, "Yeepay"); -// if (Yeepay.containsKey("channel")) { -// channels.add(Yeepay); -// } - JSONObject CBBankPay = getChannel(clientId, now, "CB_BankPay"); - if (CBBankPay.containsKey("channel")) { - channels.add(CBBankPay); + JSONObject client = clientManager.getClientInfoByMoniker(clientMoniker); + if (client == null) { + throw new NotFoundException("Client not found, please check"); } - res.put("channels", channels); - return res; + return getClientRateByClientId(client.getIntValue("client_id")); } private JSONObject getChannel(int clientId, Date date, String channel) { @@ -2250,12 +2312,11 @@ public class RetailAppServiceImp implements RetailAppService { deviceSupport.findRegister(clientType); JSONObject client = clientMapper.findClient(device.getIntValue("client_id")); JSONObject account = clientAccountMapper.findById(device.getString("account_id")); + JSONObject authFileStatus = signInAccountService.checkAuthFileStatus(client); if (PartnerRole.getRole(account.getIntValue("role")) == PartnerRole.CASHIER) { - JSONObject cashierResult = new JSONObject(); - cashierResult.put("client_less_file", false); - return cashierResult; + authFileStatus.put("client_less_file", false); } - return signInAccountService.checkAuthFileStatus(client); + return authFileStatus; } @Override @@ -2387,6 +2448,19 @@ public class RetailAppServiceImp implements RetailAppService { clientManager.commitAuthFilesToCompliance(client.getString("client_moniker"), account,"App"); } + @Override + public boolean isSubPartner(JSONObject device,String clientMoniker) { + JSONObject client = clientManager.getClientInfoByMoniker(clientMoniker); + if (client == null) { + throw new NotFoundException("Client not found, please check"); + } + if (device.getIntValue("client_id") == client.getIntValue("client_id")) { + return true; + } + JSONObject deviceClient = clientManager.getClientInfo(device.getIntValue("client_id")); + JSONArray listSubClients = clientManager.getAllClientIds(device.getIntValue("client_id")); + return (listSubClients.contains(client.getString("client_id")) && deviceClient.getBooleanValue("sub_manage")); + } private void exportCBBankAggregateFile(JSONObject client, HttpServletResponse httpResponse) { httpResponse.setContentType("application/pdf"); @@ -2444,4 +2518,84 @@ public class RetailAppServiceImp implements RetailAppService { List list = clientBankAccountMapper.clientBankAccounts(client_id); return list.isEmpty() ? new JSONObject() : list.get(0); } + + private JSONObject getClientBaseInfo(JSONObject clientWithConfig) { + JSONObject res = SignInAccountServiceImpl.clientInfoWithNoSecretInfo(clientWithConfig); + res.put("is_skip_clearing", res.getBoolean("skip_clearing")); + if ("4".equals(clientWithConfig.getString("source"))) { + res.put("refuse_remark", clientWithConfig.getString("refuse_remark")); + res.put("base_info_lack", false); + res.put("compliance_info_lack", false); + if (StringUtils.isEmpty(clientWithConfig.getString("business_structure")) || StringUtils.isEmpty(clientWithConfig.getString("logo_url")) + || StringUtils.isEmpty(clientWithConfig.getString("description")) + || ("Company".equals(clientWithConfig.getString("business_structure")) && StringUtils.isEmpty(clientWithConfig.getString("acn"))) + || (!"Company".equals(clientWithConfig.getString("business_structure")) && StringUtils.isEmpty(clientWithConfig.getString("abn"))) + || (StringUtils.isEmpty(clientWithConfig.getString("company_website")) && StringUtils.isEmpty(clientWithConfig.getString("company_photo")) + && StringUtils.isEmpty(clientWithConfig.getString("store_photo")))) { + res.put("base_info_lack", true); + + } + JSONObject file = clientManager.getAuthFiles(null, clientWithConfig.getString("client_moniker")); + for (String s : fileName) { + if (file.getString(s) == null) { + res.put("compliance_info_lack", true); + } + } + } + JSONObject clientLegal = sysClientLegalPersonMapper.findRepresentativeInfo(res.getIntValue("client_id")); + if (clientLegal != null) { + res.put("legal_person", clientLegal.getString("representative_person")); + res.put("legal_job_title", clientLegal.getString("job_title")); + res.put("legal_phone", clientLegal.getString("phone")); + res.put("legal_email", clientLegal.getString("email")); + } + return res; + } + + private JSONObject getClientRateByClientId(int clientId) { + Date now = new Date(); + JSONObject res = merchantInfoProvider.clientCurrentRate(clientId, now, "Wechat"); + JSONObject client = clientManager.getClientInfo(clientId); + Assert.notNull(client, "Client is null"); + JSONObject clientConfig = clientConfigService.find(clientId); + ArrayList channels = new ArrayList<>(); + if (clientConfig.getBigDecimal("customer_surcharge_rate") != null) { + res.put("customer_surcharge_rate", clientConfig.getBigDecimal("customer_surcharge_rate")); + } + res.put("max_customer_surcharge_rate", PlatformEnvironment.getEnv().getMaxCustomerSurchargeRate()); + JSONObject wechat = getChannel(clientId, now, "Wechat"); + if (wechat.containsKey("channel")) { + channels.add(wechat); + } + JSONObject alipay = getChannel(clientId, now, "Alipay"); + if (alipay.containsKey("channel")) { + channels.add(alipay); + } + JSONObject bestpay = getChannel(clientId, now, "Bestpay"); + if (bestpay.containsKey("channel")) { + channels.add(bestpay); + } + JSONObject jd = getChannel(clientId, now, "jd"); + if (jd.containsKey("channel")) { + channels.add(jd); + } + JSONObject AlipayOnline = getChannel(clientId, now, "AlipayOnline"); + if (AlipayOnline.containsKey("channel")) { + channels.add(AlipayOnline); + } +// JSONObject Hf = getChannel(clientId, now, "hf"); +// if (Hf.containsKey("channel")) { +// channels.add(Hf); +// } +// JSONObject Yeepay = getChannel(clientId, now, "Yeepay"); +// if (Yeepay.containsKey("channel")) { +// channels.add(Yeepay); +// } + JSONObject CBBankPay = getChannel(clientId, now, "CB_BankPay"); + if (CBBankPay.containsKey("channel")) { + channels.add(CBBankPay); + } + res.put("channels", channels); + return res; + } } diff --git a/src/main/java/au/com/royalpay/payment/manage/appclient/web/RetailAppController.java b/src/main/java/au/com/royalpay/payment/manage/appclient/web/RetailAppController.java index cf73fcc21..af347377a 100644 --- a/src/main/java/au/com/royalpay/payment/manage/appclient/web/RetailAppController.java +++ b/src/main/java/au/com/royalpay/payment/manage/appclient/web/RetailAppController.java @@ -4,6 +4,7 @@ import au.com.royalpay.payment.core.exceptions.ParamInvalidException; import au.com.royalpay.payment.manage.activities.app_index.core.AppActService; import au.com.royalpay.payment.manage.activities.monsettledelay.core.ActMonDelaySettleService; import au.com.royalpay.payment.manage.appclient.beans.AppClientBean; +import au.com.royalpay.payment.manage.appclient.beans.AppPaymentConfigBean; import au.com.royalpay.payment.manage.appclient.beans.AppQueryBean; import au.com.royalpay.payment.manage.appclient.core.RetailAppService; import au.com.royalpay.payment.manage.bill.bean.NewBillBean; @@ -223,6 +224,11 @@ public class RetailAppController { return retailAppService.getClientCurrentRateNew(device); } + @GetMapping("/current_rate_new/{clientMoniker}") + public JSONObject getClientCurrentRateNew(@ModelAttribute("RETAIL_DEVICE") JSONObject device, @PathVariable String clientMoniker) { + return retailAppService.getClientCurrentRateNewByMoniker(device, clientMoniker); + } + @PutMapping("/sign_out") public void signOut(@ModelAttribute(CommonConsts.RETAIL_DEVICE) JSONObject device) { retailAppService.sign_out(device); @@ -264,6 +270,11 @@ public class RetailAppController { return retailAppService.getClientInfo(device); } + @GetMapping("/client/{clientMoniker}") + public JSONObject getClientInfoByMoniker(@ModelAttribute(CommonConsts.RETAIL_DEVICE) JSONObject device, @PathVariable String clientMoniker) { + return retailAppService.getClientInfoByMoniker(device, clientMoniker); + } + @PutMapping("/client/apply") @ResponseBody public void updatePartnerInfo(@ModelAttribute(RETAIL_DEVICE) JSONObject device, @RequestBody ClientUpdateInfo info) { @@ -316,6 +327,11 @@ public class RetailAppController { retailAppService.updateClient(device, appClientBean); } + @PutMapping("/client/{clientMoniker}") + public void updateClientByMoniker(@ModelAttribute(CommonConsts.RETAIL_DEVICE) JSONObject device, @PathVariable String clientMoniker, @RequestBody AppClientBean appClientBean) { + retailAppService.updateClientByMoniker(device, clientMoniker, appClientBean); + } + @GetMapping("/client/file") public JSONObject getAuthFiles(@ModelAttribute(CommonConsts.RETAIL_DEVICE) JSONObject device) { return retailAppService.getAuthFiles(device); @@ -326,6 +342,11 @@ public class RetailAppController { retailAppService.uploadAuthFiles(device, filesInfo); } + @PutMapping("/client/file/{clientMoniker}") + public JSONObject getAuthFilesByMoniker(@ModelAttribute(CommonConsts.RETAIL_DEVICE) JSONObject device, @PathVariable String clientMoniker) { + return retailAppService.getAuthFilesByMoniker(device, clientMoniker); + } + @GetMapping("/daily_transactions/date/{dateStr}") public JSONObject listDailyTransactions(@PathVariable String dateStr, @RequestParam(defaultValue = "Australia/Melbourne") String timezone, @RequestParam(required = false) String app_client_ids, @RequestParam(defaultValue = "false") boolean thisdevice, @ModelAttribute(CommonConsts.RETAIL_DEVICE) JSONObject device) { @@ -515,8 +536,14 @@ public class RetailAppController { @PutMapping("/surcharge") @ResponseBody - public void changeQRCodePaySurCharge(@ModelAttribute(RETAIL_DEVICE) JSONObject device, @RequestBody UpdateSurchargeDTO updateSurchargeDTO) { - retailAppService.changeSurchargeEnable(device, updateSurchargeDTO); + public void changeQRCodePaySurCharge(@ModelAttribute(RETAIL_DEVICE) JSONObject device, @RequestBody AppPaymentConfigBean appPaymentConfigBean) { + retailAppService.changeSurchargeEnable(device, appPaymentConfigBean); + } + + @PutMapping("/surcharge/{clientMoniker}") + @ResponseBody + public void changeQRCodePaySurChargeByMoniker(@ModelAttribute(RETAIL_DEVICE) JSONObject device, @PathVariable String clientMoniker, @RequestBody AppPaymentConfigBean appPaymentConfigBean) { + retailAppService.changePaymentConfigByMoniker(device, clientMoniker,appPaymentConfigBean); } @GetMapping("/invoice") @@ -541,6 +568,11 @@ public class RetailAppController { return result; } + @GetMapping("/surcharge/{clientMoniker}") + public JSONObject changeQRCodePaySurChargeByMoniker(@ModelAttribute(RETAIL_DEVICE) JSONObject device, @PathVariable String clientMoniker) { + return retailAppService.getQRCodePaySurChargeByMoniker(device, clientMoniker); + } + @PostMapping("/attachment/files") public JSONObject uploadImage(@ModelAttribute(RETAIL_DEVICE) JSONObject device, @RequestParam MultipartFile file) throws Exception { return attachmentClient.uploadFile(file, false); diff --git a/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java b/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java index 410c93a4e..381a651b3 100644 --- a/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java +++ b/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java @@ -4127,6 +4127,11 @@ public class ClientManagerImpl implements ClientManager, ManagerTodoNoticeProvid updateObj.put("client_id", client_id); clientMapper.update(updateObj); } + JSONObject clientLegal = appClientBean.legalObject(); + if (clientLegal.size() > 0) { + clientLegal.put("client_id", client_id); + sysClientLegalPersonMapper.update(clientLegal); + } if (appClientBean.getCustomerSurchargeRate() != null) { if (appClientBean.getCustomerSurchargeRate() <= 0) { throw new ForbiddenException("customerSurchargeRate is 0"); @@ -4157,9 +4162,9 @@ public class ClientManagerImpl implements ClientManager, ManagerTodoNoticeProvid client.put("skip_clearing", skip_clearing); - if (client.getString("rpay_enterprise_id") != null) { - rpayApi.switchMerchantSettle(client); - } +// if (client.getString("rpay_enterprise_id") != null) { +// rpayApi.switchMerchantSettle(client); +// } } @Override diff --git a/src/main/java/au/com/royalpay/payment/manage/merchants/web/PartnerApplyController.java b/src/main/java/au/com/royalpay/payment/manage/merchants/web/PartnerApplyController.java index 50aff7986..05fb9148d 100644 --- a/src/main/java/au/com/royalpay/payment/manage/merchants/web/PartnerApplyController.java +++ b/src/main/java/au/com/royalpay/payment/manage/merchants/web/PartnerApplyController.java @@ -71,12 +71,6 @@ public class PartnerApplyController { return clientApply.listPartnerApply(manager,apply); } - @RequestMapping(value = "/{client_id}",method = RequestMethod.GET) - @RequireManager(role = {ManagerRole.ADMIN, ManagerRole.BD_USER, ManagerRole.OPERATOR, ManagerRole.SERVANT}) - public JSONObject getComplianceCompanyDetail(@PathVariable String client_apply_id) { - return clientApply.getPartnerApplicationDetail(client_apply_id); - } - @RequestMapping(value = "/{client_apply_id}",method = RequestMethod.GET) @RequireManager(role = {ManagerRole.ADMIN, ManagerRole.BD_USER, ManagerRole.OPERATOR, ManagerRole.SERVANT}) public JSONObject getApplicationDetail(@PathVariable String client_apply_id) { diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskBusinessService.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskBusinessService.java index a1273d3d8..920111634 100644 --- a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskBusinessService.java +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskBusinessService.java @@ -142,4 +142,6 @@ public interface RiskBusinessService { JSONObject updateIsSendClient(String riskId); void completeOrderAmount(); + + void UpdateRiskEventRemark(String riskId, String remark); } diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskBusinessServiceImpl.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskBusinessServiceImpl.java index 375532841..b911619f4 100644 --- a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskBusinessServiceImpl.java +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskBusinessServiceImpl.java @@ -1331,6 +1331,13 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo } } + @Override + public void UpdateRiskEventRemark(String riskId, String remark) { + JSONObject event = riskEventMapper.findById(riskId); + event.put("remark", remark); + riskEventMapper.update(event); + } + private List getShopTemplate() { return Arrays.asList("1.与调查交易金额对应的购物小票/发票存根照片 要求:照片应清晰,必须显示商户名称、商户地址、购物时间、物品名称、购物金额等;\n" + "Photos of shopping receipts/ invoice stubs Requirement corresponding with the investigated orders Requirement: The photos should be clear and must show Merchant name, Business address, Transaction time, Product information, Quantity purchased, etc;", diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskBusinessController.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskBusinessController.java index c4d10742c..224e3bfda 100644 --- a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskBusinessController.java +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskBusinessController.java @@ -202,5 +202,11 @@ public class RiskBusinessController { riskBusinessService.completeOrderAmount(); return "SUCCESS"; } + + @PutMapping(value = "{risk_id}/remark") + @ResponseBody + public void UpdateRiskEventRemark(@PathVariable("risk_id") String riskId, @RequestBody JSONObject remark) { + riskBusinessService.UpdateRiskEventRemark(riskId, remark.getString("remark")); + } } diff --git a/src/main/java/au/com/royalpay/payment/manage/signin/core/impls/SignInAccountServiceImpl.java b/src/main/java/au/com/royalpay/payment/manage/signin/core/impls/SignInAccountServiceImpl.java index 1cbd12d2c..2cd058a8b 100644 --- a/src/main/java/au/com/royalpay/payment/manage/signin/core/impls/SignInAccountServiceImpl.java +++ b/src/main/java/au/com/royalpay/payment/manage/signin/core/impls/SignInAccountServiceImpl.java @@ -464,7 +464,7 @@ public class SignInAccountServiceImpl implements SignInAccountService, Applicati "company_phone", "suburb", "postcode", "state", "contact_person", "contact_phone", "contact_email", "short_name", "logo_url", "enable_refund", "enable_refund_auth", "retail_surcharge", "require_custinfo", "require_remark", "logo_thumbnail", "creator", "create_time", "approver", "approve_result", "approve_time", "open_status", "timezone", "has_children", "source", "customer_surcharge_rate", "enable_alipay", "enable_wechat", - "enable_bestpay", "manual_settle", "skip_clearing", "mail_confirm", "surcharge_mode", "company_photo", "store_photo", "company_website"}; + "enable_bestpay", "manual_settle", "skip_clearing", "mail_confirm", "surcharge_mode", "company_photo", "store_photo", "company_website", "contact_job" ,"sub_manage"}; for (String col : columns) { simpleClient.put(col, client.get(col)); } @@ -644,14 +644,31 @@ public class SignInAccountServiceImpl implements SignInAccountService, Applicati JSONObject result = new JSONObject(); result.put("client_less_file", false); result.put("put_fail_pdf", "https://file.royalpay.com.au/open/2019/08/28/1566959635986_P1GuvCkuWINPhUJUqUQnz8E0u6Lgpx.pdf"); - if ((client.getIntValue("approve_result") == 2 || client.getIntValue("open_status") == 10 || client.getIntValue("approve_result") == 1 || client.getIntValue("open_status") == 5) && client.getIntValue("source")!=4) { - List clientFiles = clientFilesMapper.findAllClientFile(client.getIntValue("client_id")); - boolean clientFilesIsLess = false; - for (int i = 0; i < FILE_KEYS.length; i++) { - String fileKey = FILE_KEYS[i]; - if (clientFiles != null && clientFiles.size() > 0) { - List clientFileUrl = clientFiles.stream() - .filter(fileJson -> (fileKey.equals(fileJson.getString("file_name")) && (fileJson.getIntValue("status") == 1 || fileJson.getIntValue("status") == 2))) + List clientFiles = clientFilesMapper.findAllClientFile(client.getIntValue("client_id")); + boolean clientFilesIsLess = false; + for (int i = 0; i < FILE_KEYS.length; i++) { + String fileKey = FILE_KEYS[i]; + if (clientFiles != null && clientFiles.size() > 0) { + List clientFileUrl = clientFiles.stream() + .filter(fileJson -> (fileKey.equals(fileJson.getString("file_name")) && (fileJson.getIntValue("status") == 1 || fileJson.getIntValue("status") == 2))) + .sorted((log1, log2) -> log2.getDate("last_update_date").compareTo(log1.getDate("last_update_date"))) + .map(json -> { + JSONObject params = new JSONObject(); + params.put("file_id", json.getString("file_id")); + params.put("file_value", json.getString("file_value")); + return params; + }) + .collect(Collectors.toList()); + if (clientFileUrl != null && clientFileUrl.size() > 0) { + JSONObject fileJson = new JSONObject(); + fileJson.put("key", PUT_KEYS[i]); + fileJson.put("name", FILE_NAMES[i]); + fileJson.put("file_value", clientFileUrl); + fileJson.put("file_write", false); + result.put(fileKey,fileJson); + } else { + List clientBackToFileUrl = clientFiles.stream() + .filter(fileJson -> (fileKey.equals(fileJson.getString("file_name")) && (fileJson.getIntValue("status") == 0 || fileJson.getIntValue("status") == 3))) .sorted((log1, log2) -> log2.getDate("last_update_date").compareTo(log1.getDate("last_update_date"))) .map(json -> { JSONObject params = new JSONObject(); @@ -660,57 +677,40 @@ public class SignInAccountServiceImpl implements SignInAccountService, Applicati return params; }) .collect(Collectors.toList()); - if (clientFileUrl != null && clientFileUrl.size() > 0) { - JSONObject fileJson = new JSONObject(); - fileJson.put("key", PUT_KEYS[i]); - fileJson.put("name", FILE_NAMES[i]); - fileJson.put("file_value", clientFileUrl); - fileJson.put("file_write", false); - result.put(fileKey,fileJson); - } else { - List clientBackToFileUrl = clientFiles.stream() - .filter(fileJson -> (fileKey.equals(fileJson.getString("file_name")) && (fileJson.getIntValue("status") == 0 || fileJson.getIntValue("status") == 3))) - .sorted((log1, log2) -> log2.getDate("last_update_date").compareTo(log1.getDate("last_update_date"))) - .map(json -> { - JSONObject params = new JSONObject(); - params.put("file_id", json.getString("file_id")); - params.put("file_value", json.getString("file_value")); - return params; - }) - .collect(Collectors.toList()); - JSONObject fileJson = new JSONObject(); - fileJson.put("key", PUT_KEYS[i]); - fileJson.put("name", FILE_NAMES[i]); - if (clientBackToFileUrl != null && clientBackToFileUrl.size() > 0) { - if ("client_agree_file".equals(fileKey)) { - List agreeFile = new ArrayList<>(); - agreeFile.add(clientBackToFileUrl.get(0)); - fileJson.put("file_value", agreeFile); - }else { - fileJson.put("file_value", clientBackToFileUrl); - } + JSONObject fileJson = new JSONObject(); + fileJson.put("key", PUT_KEYS[i]); + fileJson.put("name", FILE_NAMES[i]); + if (clientBackToFileUrl != null && clientBackToFileUrl.size() > 0) { + if ("client_agree_file".equals(fileKey)) { + List agreeFile = new ArrayList<>(); + agreeFile.add(clientBackToFileUrl.get(0)); + fileJson.put("file_value", agreeFile); + }else { + fileJson.put("file_value", clientBackToFileUrl); } - fileJson.put("file_write", true); - result.put(fileKey,fileJson); - clientFilesIsLess = true; } - }else { + fileJson.put("file_write", true); + result.put(fileKey,fileJson); clientFilesIsLess = true; - for (int c = 0; c < FILE_KEYS.length; c++) { - String key = FILE_KEYS[c]; - JSONObject fileJson = new JSONObject(); - fileJson.put("key", PUT_KEYS[c]); - fileJson.put("name", FILE_NAMES[c]); - fileJson.put("file_write", true); - result.put(key,fileJson); - } } - result.put("client_less_file", clientFilesIsLess); - if (clientFilesIsLess) { - whenClientLessFile(client, result); + }else { + clientFilesIsLess = true; + for (int c = 0; c < FILE_KEYS.length; c++) { + String key = FILE_KEYS[c]; + JSONObject fileJson = new JSONObject(); + fileJson.put("key", PUT_KEYS[c]); + fileJson.put("name", FILE_NAMES[c]); + fileJson.put("file_write", true); + result.put(key,fileJson); } } } + if ((client.getIntValue("approve_result") == 2 || client.getIntValue("open_status") == 10 || client.getIntValue("approve_result") == 1 || client.getIntValue("open_status") == 5) && client.getIntValue("source")!=4 ) { + if (clientFilesIsLess) { + result.put("client_less_file", clientFilesIsLess); + whenClientLessFile(client, result); + } + } return result; } diff --git a/src/main/ui/static/analysis/risk_business.js b/src/main/ui/static/analysis/risk_business.js index 4c415f4d0..6b8fdaa79 100644 --- a/src/main/ui/static/analysis/risk_business.js +++ b/src/main/ui/static/analysis/risk_business.js @@ -279,7 +279,9 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'], app.controller('riskEventDetailCtrl', ['$scope', '$state', '$http', '$uibModal', '$filter', 'Upload', 'commonDialog', 'riskEvent', 'orderService', function ($scope, $state, $http, $uibModal, $filter, Upload, commonDialog, riskEvent, orderService) { - + $scope.ctrl = { + editRemark: false + }; $scope.orderTypes = orderTypesMap; $scope.mailTemplate = mailTemplate; $scope.resultTypes = resultTypesMap; @@ -288,6 +290,14 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'], riskEvent.data.is_send_client = true; $scope.riskEvent = riskEvent.data; + $scope.saveRemark = function () { + $http.put('/risk/business/' + $scope.riskEvent.risk_id + '/remark', {remark: $scope.riskEvent.remark}).then(function (resp) { + $scope.ctrl.editRemark = false; + }, function (resp) { + commonDialog.alert({title: 'Error', content: resp.data.message, type: 'error'}); + }) + }; + $scope.checkTemplate = function () { var url = ""; switch ($scope.riskEvent.mail_template) { diff --git a/src/main/ui/static/analysis/templates/riskEvent_detail.html b/src/main/ui/static/analysis/templates/riskEvent_detail.html index 56cc960e9..443a623b6 100644 --- a/src/main/ui/static/analysis/templates/riskEvent_detail.html +++ b/src/main/ui/static/analysis/templates/riskEvent_detail.html @@ -272,6 +272,27 @@

+
+ +
+ + + + + +
+
@@ -622,6 +643,28 @@ + +
+ +
+ +