mirror of https://github.com/jeecgboot/jeecg-boot
commit
47b8740a21
|
@ -20,6 +20,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import freemarker.template.Template;
|
import freemarker.template.Template;
|
||||||
import freemarker.template.TemplateException;
|
import freemarker.template.TemplateException;
|
||||||
|
import org.codehaus.jettison.json.JSONException;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeOrderResponse;
|
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeOrderResponse;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeWarehouseRequest;
|
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeWarehouseRequest;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeWarehouseRequestBody;
|
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeWarehouseRequestBody;
|
||||||
|
@ -385,7 +386,7 @@ public class PlatformOrderController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/orderManagement")
|
@PostMapping("/orderManagement")
|
||||||
public Result<?> orderManagement(@RequestBody List<PlatformOrderOperation> orderOperations) throws IOException {
|
public Result<?> orderManagement(@RequestBody List<PlatformOrderOperation> orderOperations) throws IOException, JSONException {
|
||||||
boolean isEmployee = securityService.checkIsEmployee();
|
boolean isEmployee = securityService.checkIsEmployee();
|
||||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||||
Client client;
|
Client client;
|
||||||
|
@ -469,6 +470,9 @@ public class PlatformOrderController {
|
||||||
}
|
}
|
||||||
List<Order> mabangOrders = platformOrderMabangService.getOrdersFromMabang(requests, executor);
|
List<Order> mabangOrders = platformOrderMabangService.getOrdersFromMabang(requests, executor);
|
||||||
for(Order mabangOrder : mabangOrders) {
|
for(Order mabangOrder : mabangOrders) {
|
||||||
|
if(mabangOrder.getTrackingNumber() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(!mabangOrder.getTrackingNumber().isEmpty()) {
|
if(!mabangOrder.getTrackingNumber().isEmpty()) {
|
||||||
ordersWithTrackingNumber.add(mabangOrder);
|
ordersWithTrackingNumber.add(mabangOrder);
|
||||||
}
|
}
|
||||||
|
@ -511,14 +515,17 @@ public class PlatformOrderController {
|
||||||
templateModel.put("lastname", client.getSurname());
|
templateModel.put("lastname", client.getSurname());
|
||||||
if(cancelCount > 0) {
|
if(cancelCount > 0) {
|
||||||
templateModel.put("cancelSuccessCount", cancelResponses.getSuccesses().size() + "/" + cancelCount);
|
templateModel.put("cancelSuccessCount", cancelResponses.getSuccesses().size() + "/" + cancelCount);
|
||||||
|
templateModel.put("cancelSuccesses", cancelResponses.getSuccesses());
|
||||||
templateModel.put("cancelFailures", cancelResponses.getFailures());
|
templateModel.put("cancelFailures", cancelResponses.getFailures());
|
||||||
}
|
}
|
||||||
if(suspendCount > 0) {
|
if(suspendCount > 0) {
|
||||||
templateModel.put("suspendSuccessCount", suspendResponses.getSuccesses().size() + "/" + suspendCount);
|
templateModel.put("suspendSuccessCount", suspendResponses.getSuccesses().size() + "/" + suspendCount);
|
||||||
|
templateModel.put("suspendSuccesses", suspendResponses.getSuccesses());
|
||||||
templateModel.put("suspendFailures", suspendResponses.getFailures());
|
templateModel.put("suspendFailures", suspendResponses.getFailures());
|
||||||
}
|
}
|
||||||
if(editCount > 0) {
|
if(editCount > 0) {
|
||||||
templateModel.put("editSuccessCount", editResponses.getSuccesses().size() + "/" + editCount);
|
templateModel.put("editSuccessCount", editResponses.getSuccesses().size() + "/" + editCount);
|
||||||
|
templateModel.put("editSuccesses", editResponses.getSuccesses());
|
||||||
templateModel.put("editFailures", editResponses.getFailures());
|
templateModel.put("editFailures", editResponses.getFailures());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,6 +546,13 @@ public class PlatformOrderController {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sync orders from Mabang
|
||||||
|
List<String> poIdsSuccesses = new ArrayList<>();
|
||||||
|
poIdsSuccesses.addAll(cancelResponses.getSuccesses());
|
||||||
|
poIdsSuccesses.addAll(suspendResponses.getSuccesses());
|
||||||
|
poIdsSuccesses.addAll(editResponses.getSuccesses());
|
||||||
|
platformOrderMabangService.syncOrdersFromMabang(poIdsSuccesses);
|
||||||
|
|
||||||
return Result.OK(result);
|
return Result.OK(result);
|
||||||
}
|
}
|
||||||
@GetMapping("/recipientInfo")
|
@GetMapping("/recipientInfo")
|
||||||
|
|
|
@ -90,6 +90,9 @@ public class SkuData {
|
||||||
public SkuStatus getStatus() {
|
public SkuStatus getStatus() {
|
||||||
return SkuStatus.fromCode(this.status);
|
return SkuStatus.fromCode(this.status);
|
||||||
}
|
}
|
||||||
|
public int getStatusValue() {
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ID : " + this.id +
|
return "ID : " + this.id +
|
||||||
"\nStockSkuId : " + this.stockSkuId +
|
"\nStockSkuId : " + this.stockSkuId +
|
||||||
|
|
|
@ -18,7 +18,7 @@ public class SkuListRequestBody implements RequestBody {
|
||||||
private String stockSku = null;
|
private String stockSku = null;
|
||||||
// 50 skus max
|
// 50 skus max
|
||||||
private String stockSkuList = null;
|
private String stockSkuList = null;
|
||||||
private DateType datetimeType;
|
private DateType datetimeType = DateType.CREATE;
|
||||||
private LocalDateTime startDate;
|
private LocalDateTime startDate;
|
||||||
private LocalDateTime endDate;
|
private LocalDateTime endDate;
|
||||||
private Integer page = 1;
|
private Integer page = 1;
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
package org.jeecg.modules.business.domain.api.mabang.doSearchSkuListNew;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.modules.business.domain.api.mabang.getorderlist.NetworkDataStream;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provide stream of order.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class SkuUpdateListStream implements NetworkDataStream<SkuData> {
|
||||||
|
|
||||||
|
private final NetworkDataStream<SkuListResponse> rawStream;
|
||||||
|
|
||||||
|
private List<SkuData> skus;
|
||||||
|
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
private boolean began;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag of current data is already empty,
|
||||||
|
* either currentOrders is null or currentIndex arrives at the end.
|
||||||
|
* In both case, we should call next() of the rawStream.
|
||||||
|
*/
|
||||||
|
private boolean empty;
|
||||||
|
|
||||||
|
|
||||||
|
public SkuUpdateListStream(NetworkDataStream<SkuListResponse> rawStream) {
|
||||||
|
this.rawStream = rawStream;
|
||||||
|
skus = null;
|
||||||
|
this.index = 0;
|
||||||
|
this.empty = true;
|
||||||
|
this.began = false;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public List<SkuData> all() {
|
||||||
|
SkuData firstElement = attempt();
|
||||||
|
if (firstElement == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<SkuData> res = new ArrayList<>();
|
||||||
|
if (firstElement.getStatus().equals(SkuStatus.Normal) || firstElement.getStatus().equals(SkuStatus.StoppedSelling)) {
|
||||||
|
res.add(firstElement);
|
||||||
|
}
|
||||||
|
while (hasNext()) {
|
||||||
|
SkuData nextSku = next();
|
||||||
|
if(nextSku.getStatus().equals(SkuStatus.Normal) || firstElement.getStatus().equals(SkuStatus.StoppedSelling)) {
|
||||||
|
res.add(nextSku);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public SkuData attempt() {
|
||||||
|
began = true;
|
||||||
|
log.info("Attempting for the first request");
|
||||||
|
SkuListResponse response = rawStream.attempt();
|
||||||
|
if (response == null) {
|
||||||
|
log.info("No response");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (response.getData().isEmpty()) {
|
||||||
|
log.info("Response with empty data");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
skus = response.getData().toJavaList(SkuData.class);
|
||||||
|
index = 1;
|
||||||
|
log.info("Returned the first element");
|
||||||
|
empty = index >= skus.size();
|
||||||
|
return skus.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
// the first time
|
||||||
|
if (!began) {
|
||||||
|
throw new IllegalStateException("Calling hasNext before begin");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current data is not yet empty
|
||||||
|
if (index < skus.size()) {
|
||||||
|
log.debug("Current order list is not empty yet");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Current data is empty */
|
||||||
|
this.empty = true;
|
||||||
|
log.debug("Current order list is already empty,");
|
||||||
|
// and raw stream is empty too.
|
||||||
|
if (!rawStream.hasNext()) {
|
||||||
|
log.debug("and source stream is empty too, hasNext: false");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// but raw stream not empty.
|
||||||
|
else {
|
||||||
|
log.debug("but source stream still has data, hasNext: true");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SkuData next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException("Stream is empty!");
|
||||||
|
}
|
||||||
|
if (empty) {
|
||||||
|
skus = this.rawStream.next().getData().toJavaList(SkuData.class);
|
||||||
|
empty = false;
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
log.debug("Return data at {}", index);
|
||||||
|
SkuData res = skus.get(index);
|
||||||
|
index++;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import org.codehaus.jettison.json.JSONObject;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ArchiveOrderRequest;
|
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ArchiveOrderRequest;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ArchiveOrderRequestBody;
|
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ArchiveOrderRequestBody;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeOrderResponse;
|
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeOrderResponse;
|
||||||
|
import org.jeecg.modules.business.service.IPlatformOrderMabangService;
|
||||||
import org.jeecg.modules.business.service.IPlatformOrderService;
|
import org.jeecg.modules.business.service.IPlatformOrderService;
|
||||||
import org.quartz.Job;
|
import org.quartz.Job;
|
||||||
import org.quartz.JobDataMap;
|
import org.quartz.JobDataMap;
|
||||||
|
@ -36,6 +37,8 @@ public class ArchiveOrderJob implements Job {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IPlatformOrderService platformOrderService;
|
private IPlatformOrderService platformOrderService;
|
||||||
|
@Autowired
|
||||||
|
private IPlatformOrderMabangService platformOrderMabangService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||||
|
@ -86,7 +89,7 @@ public class ArchiveOrderJob implements Job {
|
||||||
platformOrderIds.forEach(s -> archiveOrderRequestBodies.add(new ArchiveOrderRequestBody(s)));
|
platformOrderIds.forEach(s -> archiveOrderRequestBodies.add(new ArchiveOrderRequestBody(s)));
|
||||||
|
|
||||||
log.info("{} order archiving requests to be sent to MabangAPI", archiveOrderRequestBodies.size());
|
log.info("{} order archiving requests to be sent to MabangAPI", archiveOrderRequestBodies.size());
|
||||||
|
List<String> platformOrderIdsArchived = new ArrayList<>();
|
||||||
List<CompletableFuture<Boolean>> changeOrderFutures = archiveOrderRequestBodies.stream()
|
List<CompletableFuture<Boolean>> changeOrderFutures = archiveOrderRequestBodies.stream()
|
||||||
.map(archiveOrderRequestBody -> CompletableFuture.supplyAsync(() -> {
|
.map(archiveOrderRequestBody -> CompletableFuture.supplyAsync(() -> {
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
@ -94,7 +97,9 @@ public class ArchiveOrderJob implements Job {
|
||||||
ArchiveOrderRequest archiveOrderRequest = new ArchiveOrderRequest(archiveOrderRequestBody);
|
ArchiveOrderRequest archiveOrderRequest = new ArchiveOrderRequest(archiveOrderRequestBody);
|
||||||
ChangeOrderResponse response = archiveOrderRequest.send();
|
ChangeOrderResponse response = archiveOrderRequest.send();
|
||||||
success = response.success();
|
success = response.success();
|
||||||
} catch (RuntimeException e) {
|
if(success)
|
||||||
|
platformOrderIdsArchived.add(archiveOrderRequestBody.getPlatformOrderId());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
log.error("Error communicating with MabangAPI", e);
|
log.error("Error communicating with MabangAPI", e);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
|
@ -103,5 +108,11 @@ public class ArchiveOrderJob implements Job {
|
||||||
List<Boolean> results = changeOrderFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
List<Boolean> results = changeOrderFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||||
long nbSuccesses = results.stream().filter(b -> b).count();
|
long nbSuccesses = results.stream().filter(b -> b).count();
|
||||||
log.info("{}/{} order archiving requests have succeeded.", nbSuccesses, archiveOrderRequestBodies.size());
|
log.info("{}/{} order archiving requests have succeeded.", nbSuccesses, archiveOrderRequestBodies.size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
platformOrderMabangService.syncOrdersFromMabang(platformOrderIdsArchived);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
package org.jeecg.modules.business.domain.job;
|
package org.jeecg.modules.business.domain.job;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.codehaus.jettison.json.JSONArray;
|
import org.codehaus.jettison.json.JSONArray;
|
||||||
import org.codehaus.jettison.json.JSONException;
|
import org.codehaus.jettison.json.JSONException;
|
||||||
import org.codehaus.jettison.json.JSONObject;
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
import org.jeecg.common.api.dto.message.TemplateMessageDTO;
|
import org.jeecg.common.api.dto.message.TemplateMessageDTO;
|
||||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.getorderlist.*;
|
|
||||||
import org.jeecg.modules.business.service.IPlatformOrderMabangService;
|
import org.jeecg.modules.business.service.IPlatformOrderMabangService;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.quartz.Job;
|
import org.quartz.Job;
|
||||||
|
@ -17,10 +15,6 @@ import org.quartz.JobExecutionException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class MabangOrderSyncJob implements Job {
|
public class MabangOrderSyncJob implements Job {
|
||||||
|
@ -29,7 +23,6 @@ public class MabangOrderSyncJob implements Job {
|
||||||
private IPlatformOrderMabangService platformOrderMabangService;
|
private IPlatformOrderMabangService platformOrderMabangService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISysBaseAPI ISysBaseApi;
|
private ISysBaseAPI ISysBaseApi;
|
||||||
private static final Integer DEFAULT_NUMBER_OF_THREADS = 10;
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||||
JobDataMap jobDataMap = context.getMergedJobDataMap();
|
JobDataMap jobDataMap = context.getMergedJobDataMap();
|
||||||
|
@ -54,53 +47,32 @@ public class MabangOrderSyncJob implements Job {
|
||||||
throw new RuntimeException("PlatformOrder ID list can't be empty !");
|
throw new RuntimeException("PlatformOrder ID list can't be empty !");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Syncing following orders {}", platformOrderIds);
|
try {
|
||||||
List<List<String>> platformOrderIdLists = Lists.partition(platformOrderIds, 10);
|
JSONObject res = platformOrderMabangService.syncOrdersFromMabang(platformOrderIds);
|
||||||
List<OrderListRequestBody> requests = new ArrayList<>();
|
String syncedOrderNumber = String.valueOf(res.getInt("synced_order_number"));
|
||||||
for (List<String> platformOrderIdList : platformOrderIdLists) {
|
List<String> syncedOrderIds = new ArrayList<>();
|
||||||
requests.add(new OrderListRequestBody().setPlatformOrderIds(platformOrderIdList));
|
JSONArray syncedOrderIdsArray = res.getJSONArray("synced_order_ids");
|
||||||
}
|
for (int i = 0; i < syncedOrderIdsArray.length(); i++) {
|
||||||
List<Order> mabangOrders = new ArrayList<>();
|
syncedOrderIds.add(syncedOrderIdsArray.getString(i));
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS);
|
|
||||||
List<CompletableFuture<Boolean>> futures = requests.stream()
|
|
||||||
.map(request -> CompletableFuture.supplyAsync(() -> {
|
|
||||||
boolean success = false;
|
|
||||||
try {
|
|
||||||
OrderListRawStream rawStream = new OrderListRawStream(request);
|
|
||||||
OrderListStream stream = new OrderListStream(rawStream);
|
|
||||||
List<Order> orders = stream.all();
|
|
||||||
mabangOrders.addAll(orders);
|
|
||||||
success = !orders.isEmpty();
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
log.error("Error communicating with MabangAPI", e);
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}, executor))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
List<Boolean> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
|
||||||
long nbSuccesses = results.stream().filter(b -> b).count();
|
|
||||||
log.info("{}/{} requests have succeeded.", nbSuccesses, requests.size());
|
|
||||||
int syncedOrderNumber = mabangOrders.size();
|
|
||||||
List<String> syncedOrderIds = mabangOrders.stream().map(Order::getPlatformOrderId).collect(Collectors.toList());
|
|
||||||
log.info("{}/{} mabang orders have been retrieved.", syncedOrderNumber, platformOrderIds.size());
|
|
||||||
|
|
||||||
log.info("{} orders to be updated.", syncedOrderNumber);
|
|
||||||
platformOrderMabangService.saveOrderFromMabang(mabangOrders);
|
|
||||||
|
|
||||||
Map<String, String> param = new HashMap<>();
|
|
||||||
param.put("requested_order_number", String.valueOf(platformOrderIds.size()));
|
|
||||||
param.put("synced_order_number", String.valueOf(syncedOrderNumber));
|
|
||||||
param.put("requested_order_ids", getHtmlListFromStringList(platformOrderIds));
|
|
||||||
List<String> failedToSyncOrderIds = new ArrayList<>();
|
|
||||||
for (String platformOrderId : platformOrderIds) {
|
|
||||||
if (!syncedOrderIds.contains(platformOrderId)) {
|
|
||||||
failedToSyncOrderIds.add(platformOrderId);
|
|
||||||
}
|
}
|
||||||
|
Map<String, String> param = new HashMap<>();
|
||||||
|
param.put("requested_order_number", String.valueOf(platformOrderIds.size()));
|
||||||
|
param.put("synced_order_number", syncedOrderNumber);
|
||||||
|
param.put("requested_order_ids", getHtmlListFromStringList(platformOrderIds));
|
||||||
|
List<String> failedToSyncOrderIds = new ArrayList<>();
|
||||||
|
for (String platformOrderId : platformOrderIds) {
|
||||||
|
if (!syncedOrderIds.contains(platformOrderId)) {
|
||||||
|
failedToSyncOrderIds.add(platformOrderId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
param.put("failed_to_sync_order_ids", getHtmlListFromStringList(failedToSyncOrderIds));
|
||||||
|
TemplateMessageDTO message = new TemplateMessageDTO("admin", username == null ? "admin" : username, "马帮订单同步任务", param, "mabang_order_sync_job_result");
|
||||||
|
ISysBaseApi.sendTemplateAnnouncement(message);
|
||||||
|
log.info("Order sync job recap message sent");
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
param.put("failed_to_sync_order_ids", getHtmlListFromStringList(failedToSyncOrderIds));
|
|
||||||
TemplateMessageDTO message = new TemplateMessageDTO("admin", username == null ? "admin" : username, "马帮订单同步任务", param, "mabang_order_sync_job_result");
|
|
||||||
ISysBaseApi.sendTemplateAnnouncement(message);
|
|
||||||
log.info("Order sync job recap message sent");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
package org.jeecg.modules.business.domain.job;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.codehaus.jettison.json.JSONArray;
|
||||||
|
import org.codehaus.jettison.json.JSONException;
|
||||||
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
|
import org.jeecg.modules.business.domain.api.mabang.doSearchSkuListNew.*;
|
||||||
|
import org.jeecg.modules.business.service.ISkuListMabangService;
|
||||||
|
import org.quartz.Job;
|
||||||
|
import org.quartz.JobDataMap;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Job that retrieves all Sku from Mabang
|
||||||
|
* if the sku is of status 3 (normal) and not in DB, then we insert it in DB
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class MabangSkuSyncJob implements Job {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ISkuListMabangService skuListMabangService;
|
||||||
|
private static final Integer DEFAULT_NUMBER_OF_DAYS = 5;
|
||||||
|
private static final DateType DEFAULT_DATE_TYPE = DateType.UPDATE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||||
|
LocalDateTime endDateTime = LocalDateTime.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT")));
|
||||||
|
LocalDateTime startDateTime = endDateTime.minusDays(DEFAULT_NUMBER_OF_DAYS);
|
||||||
|
List<String> skus = new ArrayList<>();
|
||||||
|
DateType dateType = DEFAULT_DATE_TYPE;
|
||||||
|
JobDataMap jobDataMap = context.getMergedJobDataMap();
|
||||||
|
String parameter = ((String) jobDataMap.get("parameter"));
|
||||||
|
if (parameter != null) {
|
||||||
|
try {
|
||||||
|
JSONObject jsonObject = new JSONObject(parameter);
|
||||||
|
if (!jsonObject.isNull("startDateTime")) {
|
||||||
|
String startDateStr = jsonObject.getString("startDateTime");
|
||||||
|
startDateTime = LocalDateTime.parse(startDateStr);
|
||||||
|
}
|
||||||
|
if (!jsonObject.isNull("endDateTime")) {
|
||||||
|
String endDateStr = jsonObject.getString("endDateTime");
|
||||||
|
endDateTime = LocalDateTime.parse(endDateStr);
|
||||||
|
}
|
||||||
|
if (!jsonObject.isNull("dateType")) {
|
||||||
|
dateType = DateType.fromCode(jsonObject.getInt("dateType"));
|
||||||
|
}
|
||||||
|
if (!jsonObject.isNull("skus")) {
|
||||||
|
JSONArray array = jsonObject.getJSONArray("skus");
|
||||||
|
for(int i = 0; i < array.length(); i++) {
|
||||||
|
skus.add(array.getString(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
log.error("Error while parsing parameter as JSON, falling back to default parameters.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endDateTime.isAfter(startDateTime)) {
|
||||||
|
throw new RuntimeException("EndDateTime must be strictly greater than StartDateTime !");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(skus.isEmpty()) {
|
||||||
|
log.info("Updating skus by date");
|
||||||
|
while (startDateTime.until(endDateTime, ChronoUnit.HOURS) > 0) {
|
||||||
|
LocalDateTime dayBeforeEndDateTime = endDateTime.minusDays(1);
|
||||||
|
SkuListRequestBody body = SkuListRequestBodys.allSkuOfDateType(dayBeforeEndDateTime, endDateTime, dateType);
|
||||||
|
SkuListRawStream rawStream = new SkuListRawStream(body);
|
||||||
|
SkuUpdateListStream stream = new SkuUpdateListStream(rawStream);
|
||||||
|
// the status is directly filtered in all() method
|
||||||
|
List<SkuData> skusFromMabang = stream.all();
|
||||||
|
log.info("{} skus from {} to {} ({})to be updated.", skusFromMabang.size(),
|
||||||
|
dayBeforeEndDateTime, endDateTime, dateType);
|
||||||
|
|
||||||
|
if (!skusFromMabang.isEmpty()) {
|
||||||
|
// we save the skuDatas in DB
|
||||||
|
skuListMabangService.updateSkusFromMabang(skusFromMabang);
|
||||||
|
}
|
||||||
|
endDateTime = dayBeforeEndDateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.info("Updating skus by erpCode : {}", skus);
|
||||||
|
List<List<String>> skusPartition = Lists.partition(skus, 50);
|
||||||
|
for(List<String> skuPartition : skusPartition) {
|
||||||
|
SkuListRequestBody body = new SkuListRequestBody();
|
||||||
|
body.setStockSkuList(String.join(",", skuPartition));
|
||||||
|
SkuListRawStream rawStream = new SkuListRawStream(body);
|
||||||
|
SkuUpdateListStream stream = new SkuUpdateListStream(rawStream);
|
||||||
|
List<SkuData> skusFromMabang = stream.all();
|
||||||
|
log.info("{} skus to be updated.", skusFromMabang.size());
|
||||||
|
if (!skusFromMabang.isEmpty()) {
|
||||||
|
// we save the skuDatas in DB
|
||||||
|
skuListMabangService.updateSkusFromMabang(skusFromMabang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SkuListRequestErrorException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,9 +32,19 @@ public class CostTrialCalculation {
|
||||||
|
|
||||||
private final BigDecimal additionalCost;
|
private final BigDecimal additionalCost;
|
||||||
|
|
||||||
|
private final BigDecimal previousUnitPrice;
|
||||||
|
|
||||||
private CostTrialCalculation(String countryCode, String logisticsChannelName, String logisticChannelCode, BigDecimal unitPrice, BigDecimal shippingCost,
|
private final BigDecimal previousShippingCost;
|
||||||
BigDecimal registrationCost, BigDecimal additionalCost, Date effectiveDate) {
|
|
||||||
|
private final BigDecimal previousRegistrationCost;
|
||||||
|
|
||||||
|
private final BigDecimal previousAdditionalCost;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private CostTrialCalculation(String countryCode, String logisticsChannelName, String logisticChannelCode,
|
||||||
|
BigDecimal unitPrice, BigDecimal shippingCost, BigDecimal registrationCost, BigDecimal additionalCost, Date effectiveDate,
|
||||||
|
BigDecimal previousUnitPrice,BigDecimal previousShippingCost, BigDecimal previousRegistrationCost, BigDecimal previousAdditionalCost) {
|
||||||
this.countryCode = countryCode;
|
this.countryCode = countryCode;
|
||||||
this.logisticsChannelName = logisticsChannelName;
|
this.logisticsChannelName = logisticsChannelName;
|
||||||
this.logisticChannelCode = logisticChannelCode;
|
this.logisticChannelCode = logisticChannelCode;
|
||||||
|
@ -43,15 +53,28 @@ public class CostTrialCalculation {
|
||||||
this.registrationCost = registrationCost;
|
this.registrationCost = registrationCost;
|
||||||
this.additionalCost = additionalCost;
|
this.additionalCost = additionalCost;
|
||||||
this.effectiveDate = effectiveDate;
|
this.effectiveDate = effectiveDate;
|
||||||
|
this.previousUnitPrice = previousUnitPrice;
|
||||||
|
this.previousShippingCost = previousShippingCost;
|
||||||
|
this.previousRegistrationCost = previousRegistrationCost;
|
||||||
|
this.previousAdditionalCost = previousAdditionalCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CostTrialCalculation(LogisticChannelPrice price, int weight, String logisticsChannelName, String code) {
|
public CostTrialCalculation(LogisticChannelPrice price, LogisticChannelPrice previousPrice,int weight, String logisticsChannelName, String code) {
|
||||||
this(price.getEffectiveCountry(), logisticsChannelName, code, price.getCalUnitPrice(), price.calculateShippingPrice(BigDecimal.valueOf(weight)),
|
this(price.getEffectiveCountry(), logisticsChannelName, code, price.getCalUnitPrice(), price.calculateShippingPrice(BigDecimal.valueOf(weight)),
|
||||||
price.getRegistrationFee(), price.getAdditionalCost(), price.getEffectiveDate());
|
price.getRegistrationFee(), price.getAdditionalCost(), price.getEffectiveDate(),
|
||||||
|
previousPrice.getCalUnitPrice(), previousPrice.calculateShippingPrice(BigDecimal.valueOf(weight)), previousPrice.getRegistrationFee(), previousPrice.getAdditionalCost()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonProperty("TotalCost")
|
@JsonProperty("TotalCost")
|
||||||
public double getTotalCost() {
|
public double getTotalCost() {
|
||||||
return shippingCost.add(registrationCost).add(additionalCost).setScale(2, RoundingMode.CEILING).doubleValue();
|
return shippingCost.add(registrationCost).add(additionalCost).setScale(2, RoundingMode.CEILING).doubleValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonProperty("CostDifference")
|
||||||
|
public double getCostDifference() {
|
||||||
|
double previousCost = previousShippingCost.add(previousRegistrationCost).add(previousAdditionalCost).setScale(2, RoundingMode.CEILING).doubleValue();
|
||||||
|
BigDecimal diff = BigDecimal.valueOf((getTotalCost() - previousCost) / previousCost * 100);
|
||||||
|
return diff.setScale(2, RoundingMode.CEILING).doubleValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,4 +98,12 @@ public class Sku implements Serializable {
|
||||||
@Excel(name = "服务费", width = 15)
|
@Excel(name = "服务费", width = 15)
|
||||||
@ApiModelProperty(value = "服务费")
|
@ApiModelProperty(value = "服务费")
|
||||||
private java.math.BigDecimal serviceFee;
|
private java.math.BigDecimal serviceFee;
|
||||||
|
/**
|
||||||
|
* Status
|
||||||
|
* 1:自动创建;2:待开发;3:正常;4:清仓;5:停止销售"
|
||||||
|
* default : 3
|
||||||
|
*/
|
||||||
|
@Excel(name = "Status", width = 15)
|
||||||
|
@ApiModelProperty(value = "Status")
|
||||||
|
private java.lang.Integer status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ public interface LogisticChannelPriceMapper extends BaseMapper<LogisticChannelPr
|
||||||
* @param countryList the country, represented by 2 letters code
|
* @param countryList the country, represented by 2 letters code
|
||||||
* @return one propre price
|
* @return one propre price
|
||||||
*/
|
*/
|
||||||
LogisticChannelPrice findBy(
|
List<LogisticChannelPrice> findBy(
|
||||||
@Param("channelName") String channelName,
|
@Param("channelName") String channelName,
|
||||||
@Param("date") Date shippingTime,
|
@Param("date") Date shippingTime,
|
||||||
@Param("trueWeight") BigDecimal weight,
|
@Param("trueWeight") BigDecimal weight,
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
AND effective_date <= #{date}
|
AND effective_date <= #{date}
|
||||||
AND active = 1
|
AND active = 1
|
||||||
ORDER BY effective_date DESC
|
ORDER BY effective_date DESC
|
||||||
LIMIT 1 </select>
|
LIMIT 2 </select>
|
||||||
|
|
||||||
<select id="findPricesBy" resultType="org.jeecg.modules.business.entity.LogisticChannelPrice">
|
<select id="findPricesBy" resultType="org.jeecg.modules.business.entity.LogisticChannelPrice">
|
||||||
SELECT lcp.id, lcp.create_by, lcp.create_time, lcp.update_by, lcp.update_time,
|
SELECT lcp.id, lcp.create_by, lcp.create_time, lcp.update_by, lcp.update_time,
|
||||||
|
|
|
@ -209,6 +209,7 @@
|
||||||
LEFT JOIN sales_7 s7 ON s.id = s7.sku_id
|
LEFT JOIN sales_7 s7 ON s.id = s7.sku_id
|
||||||
LEFT JOIN qtyInOrdersNotShipped ON s.id = qtyInOrdersNotShipped.ID
|
LEFT JOIN qtyInOrdersNotShipped ON s.id = qtyInOrdersNotShipped.ID
|
||||||
WHERE client_sku.client_id = #{clientId}
|
WHERE client_sku.client_id = #{clientId}
|
||||||
|
AND s.status = 3
|
||||||
ORDER BY ${column} ${order}
|
ORDER BY ${column} ${order}
|
||||||
<if test="size != -1">
|
<if test="size != -1">
|
||||||
LIMIT #{offset}, #{size}
|
LIMIT #{offset}, #{size}
|
||||||
|
@ -272,6 +273,7 @@
|
||||||
LEFT JOIN sales_7 s7 ON s.id = s7.sku_id
|
LEFT JOIN sales_7 s7 ON s.id = s7.sku_id
|
||||||
LEFT JOIN qtyInOrdersNotShippedCTE ON s.id = qtyInOrdersNotShippedCTE.ID
|
LEFT JOIN qtyInOrdersNotShippedCTE ON s.id = qtyInOrdersNotShippedCTE.ID
|
||||||
WHERE client_sku.client_id = #{clientId}
|
WHERE client_sku.client_id = #{clientId}
|
||||||
|
AND s.status = 3
|
||||||
AND (
|
AND (
|
||||||
<if test="erpCodes != ''">
|
<if test="erpCodes != ''">
|
||||||
s.erp_code REGEXP #{erpCodes}
|
s.erp_code REGEXP #{erpCodes}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public interface ILogisticChannelService extends IService<LogisticChannel> {
|
||||||
* @param country 2 letters code of the destination country
|
* @param country 2 letters code of the destination country
|
||||||
* @return one suitable logistic channel price
|
* @return one suitable logistic channel price
|
||||||
*/
|
*/
|
||||||
LogisticChannelPrice findLogisticsChannelPrice(String channelName, Date date, int trueWeight, List<String> country);
|
List<LogisticChannelPrice> findLogisticsChannelPrice(String channelName, Date date, int trueWeight, List<String> country);
|
||||||
|
|
||||||
|
|
||||||
List<CostTrialCalculation> logisticChannelTrial(int weight, int volume, List<String> countryList);
|
List<CostTrialCalculation> logisticChannelTrial(int weight, int volume, List<String> countryList);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.jeecg.modules.business.service;
|
package org.jeecg.modules.business.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import org.codehaus.jettison.json.JSONException;
|
||||||
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.getorderlist.Order;
|
import org.jeecg.modules.business.domain.api.mabang.getorderlist.Order;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.getorderlist.OrderListRequestBody;
|
import org.jeecg.modules.business.domain.api.mabang.getorderlist.OrderListRequestBody;
|
||||||
import org.jeecg.modules.business.vo.PlatformOrderOperation;
|
import org.jeecg.modules.business.vo.PlatformOrderOperation;
|
||||||
|
@ -40,4 +42,6 @@ public interface IPlatformOrderMabangService extends IService<Order> {
|
||||||
void clearLogisticChannel(List<Order> orders, ExecutorService executor);
|
void clearLogisticChannel(List<Order> orders, ExecutorService executor);
|
||||||
|
|
||||||
String stripAccents(String input);
|
String stripAccents(String input);
|
||||||
|
|
||||||
|
JSONObject syncOrdersFromMabang(List<String> platformOrderIds) throws JSONException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ public interface ISkuListMabangService extends IService<SkuData> {
|
||||||
* @param skuDataList skus to save.
|
* @param skuDataList skus to save.
|
||||||
*/
|
*/
|
||||||
Map<Sku, String> saveSkuFromMabang(List<SkuData> skuDataList);
|
Map<Sku, String> saveSkuFromMabang(List<SkuData> skuDataList);
|
||||||
|
void updateSkusFromMabang(List<SkuData> skuDataList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save products to DB from mabang api.
|
* Save products to DB from mabang api.
|
||||||
|
|
|
@ -90,13 +90,16 @@ public class LogisticChannelPriceServiceImpl extends ServiceImpl<LogisticChannel
|
||||||
|
|
||||||
String countryCode = countryService.findByEnName(order.getCountry()).getCode();
|
String countryCode = countryService.findByEnName(order.getCountry()).getCode();
|
||||||
|
|
||||||
LogisticChannelPrice price = logisticChannelPriceMapper.findBy(
|
List<LogisticChannelPrice> priceList = logisticChannelPriceMapper.findBy(
|
||||||
logisticChannelName,
|
logisticChannelName,
|
||||||
order.getShippingTime(),
|
order.getShippingTime(),
|
||||||
weight,
|
weight,
|
||||||
Collections.singletonList(countryCode)
|
Collections.singletonList(countryCode)
|
||||||
);
|
);
|
||||||
|
// find the one with latest effective date
|
||||||
|
LogisticChannelPrice price = priceList.stream()
|
||||||
|
.max(Comparator.comparing(LogisticChannelPrice::getEffectiveDate))
|
||||||
|
.orElse(null);
|
||||||
if (price == null) {
|
if (price == null) {
|
||||||
throw new UserException("Can't find price for channel {}, shipped at {}, weight {}, country {}",
|
throw new UserException("Can't find price for channel {}, shipped at {}, weight {}, country {}",
|
||||||
logisticChannelName,
|
logisticChannelName,
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class LogisticChannelServiceImpl extends ServiceImpl<LogisticChannelMappe
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LogisticChannelPrice findLogisticsChannelPrice(String channelName, Date date, int trueWeight, List<String> countryList) {
|
public List<LogisticChannelPrice> findLogisticsChannelPrice(String channelName, Date date, int trueWeight, List<String> countryList) {
|
||||||
return logisticChannelPriceMapper.findBy(channelName, new java.util.Date(), BigDecimal.valueOf(trueWeight), countryList);
|
return logisticChannelPriceMapper.findBy(channelName, new java.util.Date(), BigDecimal.valueOf(trueWeight), countryList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,9 +98,15 @@ public class LogisticChannelServiceImpl extends ServiceImpl<LogisticChannelMappe
|
||||||
} else {
|
} else {
|
||||||
trueWeight = weight;
|
trueWeight = weight;
|
||||||
}
|
}
|
||||||
LogisticChannelPrice price = findLogisticsChannelPrice(channelName, new Date(), trueWeight, countryList);
|
List<LogisticChannelPrice> priceList = findLogisticsChannelPrice(channelName, new Date(), trueWeight, countryList);
|
||||||
|
LogisticChannelPrice price = priceList.stream()
|
||||||
|
.max(Comparator.comparing(LogisticChannelPrice::getEffectiveDate))
|
||||||
|
.orElse(null);
|
||||||
|
LogisticChannelPrice previousPrice = priceList.stream()
|
||||||
|
.min(Comparator.comparing(LogisticChannelPrice::getEffectiveDate))
|
||||||
|
.orElse(null);
|
||||||
if (price != null) {
|
if (price != null) {
|
||||||
return new CostTrialCalculation(price, trueWeight, internalName, code);
|
return new CostTrialCalculation(price, previousPrice, trueWeight, internalName, code);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package org.jeecg.modules.business.service.impl;
|
package org.jeecg.modules.business.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.shiro.SecurityUtils;
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.codehaus.jettison.json.JSONException;
|
||||||
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
|
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||||
import org.jeecg.common.system.vo.LoginUser;
|
import org.jeecg.common.system.vo.LoginUser;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.*;
|
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.*;
|
||||||
import org.jeecg.modules.business.domain.api.mabang.getorderlist.*;
|
import org.jeecg.modules.business.domain.api.mabang.getorderlist.*;
|
||||||
|
@ -13,6 +17,7 @@ import org.jeecg.modules.business.domain.job.ThrottlingExecutorService;
|
||||||
import org.jeecg.modules.business.entity.PlatformOrder;
|
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||||
import org.jeecg.modules.business.mapper.PlatformOrderMabangMapper;
|
import org.jeecg.modules.business.mapper.PlatformOrderMabangMapper;
|
||||||
import org.jeecg.modules.business.service.IPlatformOrderMabangService;
|
import org.jeecg.modules.business.service.IPlatformOrderMabangService;
|
||||||
|
import org.jeecg.modules.business.service.IPlatformOrderService;
|
||||||
import org.jeecg.modules.business.vo.PlatformOrderOperation;
|
import org.jeecg.modules.business.vo.PlatformOrderOperation;
|
||||||
import org.jeecg.modules.business.vo.Responses;
|
import org.jeecg.modules.business.vo.Responses;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -23,6 +28,7 @@ import java.text.Normalizer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -40,6 +46,10 @@ import static java.util.stream.Collectors.toList;
|
||||||
public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMabangMapper, Order> implements IPlatformOrderMabangService {
|
public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMabangMapper, Order> implements IPlatformOrderMabangService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlatformOrderMabangMapper platformOrderMabangMapper;
|
private PlatformOrderMabangMapper platformOrderMabangMapper;
|
||||||
|
@Autowired
|
||||||
|
private IPlatformOrderService orderservice;
|
||||||
|
@Autowired
|
||||||
|
private ISysBaseAPI ISysBaseApi;
|
||||||
|
|
||||||
private static final Integer DEFAULT_NUMBER_OF_THREADS = 2;
|
private static final Integer DEFAULT_NUMBER_OF_THREADS = 2;
|
||||||
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 10;
|
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 10;
|
||||||
|
@ -92,6 +102,9 @@ public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMab
|
||||||
) {
|
) {
|
||||||
// If order wasn't invoiced pre-shipping, we can remove and re-insert contents
|
// If order wasn't invoiced pre-shipping, we can remove and re-insert contents
|
||||||
if (orderInDatabase.getShippingInvoiceNumber() == null) {
|
if (orderInDatabase.getShippingInvoiceNumber() == null) {
|
||||||
|
boolean hasInvoiceNumber = orderservice.getById(orderInDatabase.getId()).getShippingInvoiceNumber() != null;
|
||||||
|
if(hasInvoiceNumber)
|
||||||
|
continue;
|
||||||
oldOrders.add(retrievedOrder);
|
oldOrders.add(retrievedOrder);
|
||||||
} else {
|
} else {
|
||||||
invoicedShippedOrders.add(retrievedOrder);
|
invoicedShippedOrders.add(retrievedOrder);
|
||||||
|
@ -116,11 +129,11 @@ public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMab
|
||||||
/* for new orders, insert them to DB and their children */
|
/* for new orders, insert them to DB and their children */
|
||||||
List<OrderItem> allNewItems = prepareItems(newOrders);
|
List<OrderItem> allNewItems = prepareItems(newOrders);
|
||||||
try {
|
try {
|
||||||
if (newOrders.size() != 0) {
|
if (!newOrders.isEmpty()) {
|
||||||
log.info("{} orders to be inserted/updated.", newOrders.size());
|
log.info("{} orders to be inserted/updated.", newOrders.size());
|
||||||
platformOrderMabangMapper.insertOrdersFromMabang(newOrders);
|
platformOrderMabangMapper.insertOrdersFromMabang(newOrders);
|
||||||
}
|
}
|
||||||
if (allNewItems.size() != 0) {
|
if (!allNewItems.isEmpty()) {
|
||||||
platformOrderMabangMapper.insertOrderItemsFromMabang(allNewItems);
|
platformOrderMabangMapper.insertOrderItemsFromMabang(allNewItems);
|
||||||
log.info("{} order items to be inserted/updated.", allNewItems.size());
|
log.info("{} order items to be inserted/updated.", allNewItems.size());
|
||||||
}
|
}
|
||||||
|
@ -131,12 +144,12 @@ public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMab
|
||||||
// for old orders, update themselves and delete and reinsert their content.
|
// for old orders, update themselves and delete and reinsert their content.
|
||||||
List<OrderItem> allNewItemsOfOldItems = prepareItems(oldOrders);
|
List<OrderItem> allNewItemsOfOldItems = prepareItems(oldOrders);
|
||||||
try {
|
try {
|
||||||
if (oldOrders.size() != 0) {
|
if (!oldOrders.isEmpty()) {
|
||||||
log.info("{} orders to be inserted/updated.", oldOrders.size());
|
log.info("{} orders to be inserted/updated.", oldOrders.size());
|
||||||
platformOrderMabangMapper.batchUpdateById(oldOrders);
|
platformOrderMabangMapper.batchUpdateById(oldOrders);
|
||||||
platformOrderMabangMapper.batchDeleteByMainID(oldOrders.stream().map(Order::getId).collect(toList()));
|
platformOrderMabangMapper.batchDeleteByMainID(oldOrders.stream().map(Order::getId).collect(toList()));
|
||||||
}
|
}
|
||||||
if (ordersFromShippedToCompleted.size() != 0) {
|
if (!ordersFromShippedToCompleted.isEmpty()) {
|
||||||
log.info("{} orders to be updated from Shipped to Completed.", ordersFromShippedToCompleted.size());
|
log.info("{} orders to be updated from Shipped to Completed.", ordersFromShippedToCompleted.size());
|
||||||
platformOrderMabangMapper.batchUpdateById(ordersFromShippedToCompleted);
|
platformOrderMabangMapper.batchUpdateById(ordersFromShippedToCompleted);
|
||||||
log.info("Contents of {} orders to be updated from Shipped to Completed.", ordersFromShippedToCompleted.size());
|
log.info("Contents of {} orders to be updated from Shipped to Completed.", ordersFromShippedToCompleted.size());
|
||||||
|
@ -144,7 +157,7 @@ public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMab
|
||||||
ordersFromShippedToCompleted.stream().map(Order::getId).collect(toList()),
|
ordersFromShippedToCompleted.stream().map(Order::getId).collect(toList()),
|
||||||
OrderStatus.Completed.getCode());
|
OrderStatus.Completed.getCode());
|
||||||
}
|
}
|
||||||
if (invoicedShippedOrders.size() != 0) {
|
if (!invoicedShippedOrders.isEmpty()) {
|
||||||
log.info("{} orders to be updated from Pending/Preparing to Shipped.", invoicedShippedOrders.size());
|
log.info("{} orders to be updated from Pending/Preparing to Shipped.", invoicedShippedOrders.size());
|
||||||
platformOrderMabangMapper.batchUpdateById(invoicedShippedOrders);
|
platformOrderMabangMapper.batchUpdateById(invoicedShippedOrders);
|
||||||
log.info("Contents of {} orders to be updated from Pending/Preparing to Shipped.", invoicedShippedOrders.size());
|
log.info("Contents of {} orders to be updated from Pending/Preparing to Shipped.", invoicedShippedOrders.size());
|
||||||
|
@ -152,7 +165,7 @@ public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMab
|
||||||
invoicedShippedOrders.stream().map(Order::getId).collect(toList()),
|
invoicedShippedOrders.stream().map(Order::getId).collect(toList()),
|
||||||
OrderStatus.Shipped.getCode());
|
OrderStatus.Shipped.getCode());
|
||||||
}
|
}
|
||||||
if (obsoleteOrders.size() != 0) {
|
if (!obsoleteOrders.isEmpty()) {
|
||||||
log.info("{} orders to become obsolete.", obsoleteOrders.size());
|
log.info("{} orders to become obsolete.", obsoleteOrders.size());
|
||||||
platformOrderMabangMapper.batchUpdateById(obsoleteOrders);
|
platformOrderMabangMapper.batchUpdateById(obsoleteOrders);
|
||||||
log.info("Contents of {} orders to be updated to Obsolete.", obsoleteOrders.size());
|
log.info("Contents of {} orders to be updated to Obsolete.", obsoleteOrders.size());
|
||||||
|
@ -160,7 +173,7 @@ public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMab
|
||||||
obsoleteOrders.stream().map(Order::getId).collect(toList()),
|
obsoleteOrders.stream().map(Order::getId).collect(toList()),
|
||||||
OrderStatus.Obsolete.getCode());
|
OrderStatus.Obsolete.getCode());
|
||||||
}
|
}
|
||||||
if (allNewItemsOfOldItems.size() != 0) {
|
if (!allNewItemsOfOldItems.isEmpty()) {
|
||||||
log.info("{} order items to be inserted/updated.", allNewItemsOfOldItems.size());
|
log.info("{} order items to be inserted/updated.", allNewItemsOfOldItems.size());
|
||||||
platformOrderMabangMapper.insertOrderItemsFromMabang(allNewItemsOfOldItems);
|
platformOrderMabangMapper.insertOrderItemsFromMabang(allNewItemsOfOldItems);
|
||||||
}
|
}
|
||||||
|
@ -324,4 +337,46 @@ public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMab
|
||||||
input = input.replaceAll("[^\\p{ASCII}]", "");
|
input = input.replaceAll("[^\\p{ASCII}]", "");
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public JSONObject syncOrdersFromMabang(List<String> platformOrderIds) throws JSONException {
|
||||||
|
log.info("Syncing following orders {}", platformOrderIds);
|
||||||
|
List<List<String>> platformOrderIdLists = Lists.partition(platformOrderIds, 10);
|
||||||
|
List<OrderListRequestBody> requests = new ArrayList<>();
|
||||||
|
for (List<String> platformOrderIdList : platformOrderIdLists) {
|
||||||
|
requests.add(new OrderListRequestBody().setPlatformOrderIds(platformOrderIdList));
|
||||||
|
}
|
||||||
|
List<Order> mabangOrders = new ArrayList<>();
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(10);
|
||||||
|
List<CompletableFuture<Boolean>> futures = requests.stream()
|
||||||
|
.map(request -> CompletableFuture.supplyAsync(() -> {
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
OrderListRawStream rawStream = new OrderListRawStream(request);
|
||||||
|
OrderListStream stream = new OrderListStream(rawStream);
|
||||||
|
List<Order> orders = stream.all();
|
||||||
|
mabangOrders.addAll(orders);
|
||||||
|
success = !orders.isEmpty();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("Error communicating with MabangAPI", e);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}, executor))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<Boolean> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||||
|
long nbSuccesses = results.stream().filter(b -> b).count();
|
||||||
|
log.info("{}/{} requests have succeeded.", nbSuccesses, requests.size());
|
||||||
|
int syncedOrderNumber = mabangOrders.size();
|
||||||
|
List<String> syncedOrderIds = mabangOrders.stream().map(Order::getPlatformOrderId).collect(Collectors.toList());
|
||||||
|
log.info("{}/{} mabang orders have been retrieved.", syncedOrderNumber, platformOrderIds.size());
|
||||||
|
|
||||||
|
log.info("{} orders to be updated.", syncedOrderNumber);
|
||||||
|
saveOrderFromMabang(mabangOrders);
|
||||||
|
|
||||||
|
JSONObject res = new JSONObject();
|
||||||
|
res.put("synced_order_number", syncedOrderNumber);
|
||||||
|
res.put("synced_order_ids", syncedOrderIds);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,10 @@ public class SkuListMabangServiceImpl extends ServiceImpl<SkuListMabangMapper, S
|
||||||
@Autowired
|
@Autowired
|
||||||
Environment env;
|
Environment env;
|
||||||
|
|
||||||
|
// In NameCN field on top of the product name we also get the customer code in the beginning of the string : "XX Description of the product"
|
||||||
|
final Pattern cnNamePattern = Pattern.compile("^([a-zA-Z]{2,5})\\s(.*)$");
|
||||||
|
// In NameEN field on top of the product name we also get the customer code in the beginning of the string : "XX-En name of product"
|
||||||
|
final Pattern enNamePattern = Pattern.compile("^([a-zA-Z]{2,5})-(.*)$");
|
||||||
/**
|
/**
|
||||||
* Save skus to DB from mabang api.
|
* Save skus to DB from mabang api.
|
||||||
*
|
*
|
||||||
|
@ -94,7 +97,7 @@ public class SkuListMabangServiceImpl extends ServiceImpl<SkuListMabangMapper, S
|
||||||
|
|
||||||
/* for new skuDatas, insert them to DB */
|
/* for new skuDatas, insert them to DB */
|
||||||
try {
|
try {
|
||||||
if (newSkuDatas.size() != 0) {
|
if (!newSkuDatas.isEmpty()) {
|
||||||
// we need to check if the product associated with the sku exists, for that we are going to parse the Sku erpCode into product code
|
// we need to check if the product associated with the sku exists, for that we are going to parse the Sku erpCode into product code
|
||||||
// check if the product code exists in DB, if not we create a new entry in DB and fill all the infos.
|
// check if the product code exists in DB, if not we create a new entry in DB and fill all the infos.
|
||||||
// then we can finally add the new Sku, product has to be created first if it doesn't exist, since we need to fill productID in Sku table
|
// then we can finally add the new Sku, product has to be created first if it doesn't exist, since we need to fill productID in Sku table
|
||||||
|
@ -160,6 +163,63 @@ public class SkuListMabangServiceImpl extends ServiceImpl<SkuListMabangMapper, S
|
||||||
return newSkusMap;
|
return newSkusMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void updateSkusFromMabang(List<SkuData> skuDataList) {
|
||||||
|
if (skuDataList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we collect all erpCode
|
||||||
|
List<String> allSkuErpCode = skuDataList.stream()
|
||||||
|
.map(SkuData::getErpCode)
|
||||||
|
.collect(toList());
|
||||||
|
// find Skus that already exist in DB
|
||||||
|
List<Sku> existingSkuList = skuListMabangMapper.searchExistence(allSkuErpCode);
|
||||||
|
// We map all existing Skus in DB with erpCode as key
|
||||||
|
Map<String, Sku> existingSkusIDMap = existingSkuList.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
Sku::getErpCode, Function.identity()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
ArrayList<SkuData> existingSkuDatas = new ArrayList<>();
|
||||||
|
for (SkuData retrievedSkuData : skuDataList) {
|
||||||
|
Sku skuInDatabase = existingSkusIDMap.get(retrievedSkuData.getErpCode());
|
||||||
|
// the current SkuData's erpCode is in DB, so we add it to the list of existingSkuDatas
|
||||||
|
if (skuInDatabase != null) {
|
||||||
|
existingSkuDatas.add(retrievedSkuData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for skuDatas to update, update product names and sku status them to DB */
|
||||||
|
try {
|
||||||
|
if (!existingSkuDatas.isEmpty()) {
|
||||||
|
// we need to check if the product associated with the sku exists, for that we are going to parse the Sku erpCode into product code
|
||||||
|
// check if the product code exists in DB, if not we create a new entry in DB and fill all the infos.
|
||||||
|
// then we can finally add the new Sku, product has to be created first if it doesn't exist, since we need to fill productID in Sku table
|
||||||
|
// we can now proceed to create new sku_declare_value associated with the new Sku and also sku_price
|
||||||
|
updateProductFromMabang(existingSkuDatas);
|
||||||
|
log.info("{} skus to be updated.", existingSkuDatas.size());
|
||||||
|
|
||||||
|
//update status of existing skus
|
||||||
|
List<Sku> skusToUpdate = new ArrayList<>();
|
||||||
|
for(SkuData skuData: existingSkuDatas) {
|
||||||
|
Sku s = new Sku();
|
||||||
|
s.setId(existingSkusIDMap.get(skuData.getErpCode()).getId());
|
||||||
|
s.setUpdateBy("mabang api");
|
||||||
|
s.setUpdateTime(new Date());
|
||||||
|
s.setStatus(skuData.getStatusValue());
|
||||||
|
skusToUpdate.add(s);
|
||||||
|
}
|
||||||
|
skuService.updateBatchById(skusToUpdate);
|
||||||
|
log.info("Updated {} skus : {}.", skusToUpdate.size(), existingSkuDatas.stream().map(SkuData::getErpCode).collect(toList()));
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save products to DB from mabang api.
|
* Save products to DB from mabang api.
|
||||||
*
|
*
|
||||||
|
@ -197,6 +257,63 @@ public class SkuListMabangServiceImpl extends ServiceImpl<SkuListMabangMapper, S
|
||||||
}
|
}
|
||||||
return productsWithInfoMap;
|
return productsWithInfoMap;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Update products enName and zhName to DB from mabang api.
|
||||||
|
*
|
||||||
|
* @param skuDataList we update the product enName and zhName of the skus
|
||||||
|
*/
|
||||||
|
public void updateProductFromMabang(List<SkuData> skuDataList) {
|
||||||
|
List<String> allProductCodes = parseSkuListToProductCodeList(skuDataList);
|
||||||
|
|
||||||
|
List< Product> existingProduct = skuListMabangMapper.searchProductExistence(allProductCodes);
|
||||||
|
Map<String, Product> existingProductsIDMap = existingProduct.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
Product::getCode, Function.identity()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
List<SkuData> skuDatasToUpdate = new ArrayList<>();
|
||||||
|
// we ignore the new products and only update the existing ones
|
||||||
|
for(SkuData skuData : skuDataList) {
|
||||||
|
Product productInDB = existingProductsIDMap.get(parseSkuToProduct(skuData.getErpCode()));
|
||||||
|
// the current product code is in DB, so we add it to the list of newProducts
|
||||||
|
if (productInDB != null) {
|
||||||
|
skuDatasToUpdate.add(skuData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Product> productsToUpdate = new ArrayList<>();
|
||||||
|
for(SkuData skuData: skuDatasToUpdate) {
|
||||||
|
Product p = new Product();
|
||||||
|
p.setId(existingProductsIDMap.get(parseSkuToProduct(skuData.getErpCode())).getId());
|
||||||
|
p.setUpdateBy("mabang api");
|
||||||
|
p.setUpdateTime(new Date());
|
||||||
|
// Removing the customer code from the product CN name
|
||||||
|
if (!skuData.getNameEN().isEmpty()) {
|
||||||
|
Matcher enNameMatcher = enNamePattern.matcher(skuData.getNameEN());
|
||||||
|
if (enNameMatcher.matches() && !enNameMatcher.group(2).isEmpty()) {
|
||||||
|
p.setEnName(enNameMatcher.group(2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.setEnName(skuData.getNameEN());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Removing the customer code from the product CN name
|
||||||
|
if (!skuData.getNameCN().isEmpty()) {
|
||||||
|
Matcher cnNameMatcher = cnNamePattern.matcher(skuData.getNameCN());
|
||||||
|
if (cnNameMatcher.matches() && !cnNameMatcher.group(2).isEmpty()) {
|
||||||
|
p.setZhName(cnNameMatcher.group(2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.setZhName(skuData.getNameCN());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
productsToUpdate.add(p);
|
||||||
|
}
|
||||||
|
if(!productsToUpdate.isEmpty()) {
|
||||||
|
productService.updateBatchById(productsToUpdate);
|
||||||
|
}
|
||||||
|
log.info("Updated {} products : {}.", productsToUpdate.size(), skuDatasToUpdate.stream().map(SkuData::getErpCode).collect(toList()));
|
||||||
|
}
|
||||||
|
|
||||||
public void saveSkuPrices(List<SkuData> newSkus) {
|
public void saveSkuPrices(List<SkuData> newSkus) {
|
||||||
List<SkuPrice> l = new ArrayList<>();
|
List<SkuPrice> l = new ArrayList<>();
|
||||||
|
@ -290,8 +407,6 @@ public class SkuListMabangServiceImpl extends ServiceImpl<SkuListMabangMapper, S
|
||||||
final String electroMagSensitiveAttributeId = skuListMabangMapper.searchSensitiveAttributeId("Electro-magnetic");
|
final String electroMagSensitiveAttributeId = skuListMabangMapper.searchSensitiveAttributeId("Electro-magnetic");
|
||||||
final String electricSensitiveAttributeId = skuListMabangMapper.searchSensitiveAttributeId("Electronic/Electric");
|
final String electricSensitiveAttributeId = skuListMabangMapper.searchSensitiveAttributeId("Electronic/Electric");
|
||||||
final String normalSensitiveAttributeId = skuListMabangMapper.searchSensitiveAttributeId("Normal goods");
|
final String normalSensitiveAttributeId = skuListMabangMapper.searchSensitiveAttributeId("Normal goods");
|
||||||
// In NameCN field on top of the product name we also get the customer code in the beginning of the string : "XX Description of the product"
|
|
||||||
final Pattern cnNamePattern = Pattern.compile("^([a-zA-Z]{2,5})\\s(.*)$");
|
|
||||||
// IN saleRemark sometimes not only the product weight provided, we can get extra information such as service_fee (eg : "15每件服务费0.2")
|
// IN saleRemark sometimes not only the product weight provided, we can get extra information such as service_fee (eg : "15每件服务费0.2")
|
||||||
final Pattern saleRemarkPattern = Pattern.compile("^([0-9]*)(.*)$");
|
final Pattern saleRemarkPattern = Pattern.compile("^([0-9]*)(.*)$");
|
||||||
// we are stocking product codes, so we don't get duplicates which causes error
|
// we are stocking product codes, so we don't get duplicates which causes error
|
||||||
|
|
|
@ -7,7 +7,13 @@
|
||||||
</tr>
|
</tr>
|
||||||
<#if cancelSuccessCount??>
|
<#if cancelSuccessCount??>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:0 0 35px 0;">Demandes d'annulations de commande : <b>${cancelSuccessCount}</b> réussie(s)</td>
|
<td style="padding:0 0 35px 0;">Demandes d'annulations de commande : <b>${cancelSuccessCount}</b> réussie(s) :
|
||||||
|
<ul>
|
||||||
|
<#list cancelSuccesses as success>
|
||||||
|
<li>${success}</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<#if cancelFailures?size gt 0 >
|
<#if cancelFailures?size gt 0 >
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -23,7 +29,13 @@
|
||||||
</#if>
|
</#if>
|
||||||
<#if suspendSuccessCount??>
|
<#if suspendSuccessCount??>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:0 0 35px 0;">Demandes de suspension de commande : <b>${suspendSuccessCount}</b> réussie(s)</td>
|
<td style="padding:0 0 35px 0;">Demandes de suspension de commande : <b>${suspendSuccessCount}</b> réussie(s) :
|
||||||
|
<ul>
|
||||||
|
<#list suspendSuccesses as success>
|
||||||
|
<li>${success}</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<#if suspendFailures?size gt 0 >
|
<#if suspendFailures?size gt 0 >
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -39,7 +51,13 @@
|
||||||
</#if>
|
</#if>
|
||||||
<#if editSuccessCount??>
|
<#if editSuccessCount??>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:0 0 35px 0;">Demandes de modification d'informations de commande : <b>${editSuccessCount}</b> réussie(s)</td>
|
<td style="padding:0 0 35px 0;">Demandes de modification d'informations de commande : <b>${editSuccessCount}</b> réussie(s) :
|
||||||
|
<ul>
|
||||||
|
<#list editSuccesses as success>
|
||||||
|
<li>${success}</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<#if editFailures?size gt 0 >
|
<#if editFailures?size gt 0 >
|
||||||
<tr>
|
<tr>
|
||||||
|
|
Loading…
Reference in New Issue