feature : (WIP) Expenses Overview + LogisticChannelChoice job

pull/6221/head
Gauthier LO 2023-10-09 16:38:43 +02:00
parent cff04fe906
commit 9d765591a9
48 changed files with 1181 additions and 68 deletions

54
db/views/transaction.sql Normal file
View File

@ -0,0 +1,54 @@
CREATE VIEW transaction as
SELECT id, create_by, create_time, update_by, update_time,
type, client_id, payment_proof, invoice_number, shipping_fee, purchase_fee, amount, currency
FROM
(
SELECT id as id,
create_by,
create_time,
update_by,
update_time,
'Credit' as type,
client_id,
payment_proof,
NULL as invoice_number,
NULL as shipping_fee,
NULL as purchase_fee,
amount as amount,
(SELECT code FROM currency WHERE credit.currency_id = id) as currency
FROM credit
UNION ALL
SELECT id as id,
create_by,
create_time,
update_by,
update_time,
'Debit' as type,
client_id,
NULL as payment_proof,
invoice_number,
total_amount as shipping_fee,
IF(invoice_number LIKE '%%%%-%%-7%%%',
purchase_total(invoice_number),
NULL) as purchase_fee,
IF(invoice_number LIKE '%%%%-%%-7%%%',
total_amount + (purchase_total(invoice_number)),
total_amount) as amount,
(SELECT code FROM currency WHERE shipping_invoice.currency_id = id) as currency
FROM shipping_invoice
WHERE client_id IS NOT NULL
AND shipping_invoice.currency_id IS NOT NULL
AND shipping_invoice.currency_id <> ''
) as id;
-- Function that computes the total of purchase by order
CREATE FUNCTION purchase_total( invoice_number varchar(12)) RETURNS decimal(10, 2)
BEGIN
RETURN (
SELECT SUM(poc.purchase_fee) as total
FROM platform_order_content as poc
JOIN platform_order po
ON po.id = poc.platform_order_id
WHERE po.shipping_invoice_number = invoice_number
);
END;

View File

@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -35,31 +36,35 @@ public class UserClientController {
private IShopService shopService;
@Autowired
private IPlatformOrderService platformOrderService;
/**
* Checks if the user is a client and then lists orders of erp_status 1
* @return
* Checks if the user is a client or internal user
* @return the client's info OR a list of clients
*/
@GetMapping(value = "/getClient")
public Result<?> getClientByUserId() {
LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = loginUser.getId();
userId = "1698676211346821122";
userId = "1708866308713140225";
Client client = userClientService.getClientByUserId(userId);
if(client == null) {
List<SysRole> sysRoles = sysUserRoleService.getUserRole(userId);
boolean isAllowed = false;
for(SysRole sysRole: sysRoles) {
if(sysRole.getRoleCode().equals("admin") || sysRole.getRoleCode().equals("dev")) {
if(sysRole.getRoleCode().equals("admin") || sysRole.getRoleCode().equals("dev") || sysRole.getRoleCode().equals("Sales")){
isAllowed = true;
break;
}
}
if(isAllowed)
return Result.OK("Permission Granted", "admin");
if(isAllowed) {
Map<String, List<Client>> internalClientList = new HashMap<>();
internalClientList.put("internal", userClientService.listClients());
return Result.OK("internal usage", internalClientList);
}
else
return Result.error(403, "Access Denied.");
}
return Result.ok(client);
Map<String, Client> clientMap = new HashMap<>();
clientMap.put("client", client);
return Result.ok(clientMap);
}
}

View File

@ -5,8 +5,11 @@ import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.business.domain.shippingInvoice.ShippingInvoiceFactory;
import org.jeecg.modules.business.entity.PlatformOrder;
import org.jeecg.modules.business.entity.PlatformOrderContent;
import org.jeecg.modules.business.entity.SkuPrice;
import org.jeecg.modules.business.mapper.*;
import org.jeecg.modules.business.service.*;
import org.jeecg.modules.business.vo.Estimation;
import org.jeecg.modules.business.vo.ShippingFeesEstimation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@ -14,9 +17,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
@Api(tags = "Transaction")
@ -35,6 +38,8 @@ public class TransactionController {
@Autowired
PlatformOrderMapper platformOrderMapper;
@Autowired
PlatformOrderContentMapper platformOrderContentMapper;
@Autowired
ClientMapper clientMapper;
@Autowired
ShopMapper shopMapper;
@ -66,12 +71,12 @@ public class TransactionController {
public Result<?> list() {
return Result.ok(transactionMapper.list());
}
@GetMapping(value="/listByClient")
public Result<?> listByClientId(@RequestParam("clientId") String clientId) {
return Result.ok(transactionMapper.listByClientId(clientId));
@GetMapping(value="/listByClientAndCurrency")
public Result<?> listByClientId(@RequestParam("clientId") String clientId, @RequestParam("currency") String currency) {
return Result.ok(transactionMapper.listByClientIdAndCurrency(clientId, currency));
}
@GetMapping(value="/debit")
public Result<?> debit(@RequestParam("clientId") String clientId) {
public Result<?> debit(@RequestParam("clientId") String clientId, @RequestParam("currency") String currency) {
List<String> errorMessages = new ArrayList<>();
List<String> shopIds = shopService.listIdByClient(clientId);
List<PlatformOrder> orders = platformOrderService.findUninvoicedOrdersByShopForClient(shopIds, Arrays.asList(1,2,3));
@ -82,11 +87,43 @@ public class TransactionController {
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService);
List<ShippingFeesEstimation> estimations = factory.getEstimations(clientId, orderIds, errorMessages);
System.out.println("Estimation size : " + estimations.size());
for(ShippingFeesEstimation estimation: estimations) {
List<ShippingFeesEstimation> shippingFeesEstimations = factory.getEstimations(clientId, orderIds, errorMessages);
System.out.println("Estimation size : " + shippingFeesEstimations.size());
for(ShippingFeesEstimation estimation: shippingFeesEstimations) {
System.out.println("estimation : " + estimation.getDueForProcessedOrders());
}
return Result.ok(estimations);
// purchase estimation
List<String> estimationOrderIds = new ArrayList<>();
BigDecimal shippingFeesEstimation = BigDecimal.ZERO;
for(ShippingFeesEstimation estimation: shippingFeesEstimations) {
estimationOrderIds.addAll(estimation.getOrderIds());
shippingFeesEstimation = shippingFeesEstimation.add(estimation.getDueForProcessedOrders());
}
System.out.println("Estimation order ids : " + estimationOrderIds);
List<PlatformOrderContent> orderContents = platformOrderContentMapper.fetchOrderContent(estimationOrderIds);
List<String> skuIds = orderContents.stream().map(PlatformOrderContent::getSkuId).collect(Collectors.toList());
List<SkuPrice> skuPrices = platformOrderContentMapper.searchSkuPrice(skuIds);
BigDecimal exchangeRateEurToRmb = exchangeRatesMapper.getLatestExchangeRate("EUR", "RMB");
BigDecimal purchaseEstimation = BigDecimal.ZERO;
for(PlatformOrderContent content : orderContents){
for (SkuPrice skuPrice : skuPrices) {
if(content.getSkuId().equals(skuPrice.getSkuId())) {
purchaseEstimation = purchaseEstimation.add(skuPrice.getPrice(content.getQuantity(), exchangeRateEurToRmb));
}
}
}
if(!currency.equals("EUR")) {
BigDecimal exchangeRate = exchangeRatesMapper.getLatestExchangeRate("EUR", currency);
System.out.println("Exchange rate : " + exchangeRate);
System.out.println("Purchase Fee : " + purchaseEstimation);
System.out.println("Shipping Fee : " + shippingFeesEstimation);
purchaseEstimation = purchaseEstimation.multiply(exchangeRate).setScale(2, RoundingMode.CEILING);
shippingFeesEstimation = shippingFeesEstimation.multiply(exchangeRate).setScale(2, RoundingMode.CEILING);
System.out.println("Purchase Fee " + currency + " : " + purchaseEstimation);
System.out.println("Shipping Fee " + currency + " : " + shippingFeesEstimation);
}
return Result.ok(new Estimation(shippingFeesEstimation, purchaseEstimation, currency, errorMessages));
}
}

View File

@ -0,0 +1,314 @@
package org.jeecg.modules.business.domain.job;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.mapper.PlatformOrderContentMapper;
import org.jeecg.modules.business.service.*;
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.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toMap;
/**
* @Description: A job that fills all 'invoice_logistic_channel_name' field with 'NULL' value with a default channel
* @Author: jeecg-boot
* @Date: 2023-10-03
* @Version: V1.0
*/
@Slf4j
@Component
public class LogisticChannelChoiceJob implements Job {
@Autowired
private CountryService countryService;
@Autowired
private EmailService emailService;
@Autowired
private ILogisticChannelChoiceService logisticChannelChoiceService;
@Autowired
private ILogisticChannelService logisticChannelService;
@Autowired
private IPlatformOrderService platformOrderService;
@Autowired
private IPlatformOrderContentService platformOrderContentService;
@Autowired
private ISensitiveAttributeService sensitiveAttributeService;
@Autowired
private PlatformOrderContentMapper platformOrderContentMapper;
@Autowired
Environment env;
@Autowired
private FreeMarkerConfigurer freemarkerConfigurer;
private static final Integer DEFAULT_NUMBER_OF_DAYS = 180;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
LocalDate endDate = LocalDate.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT")));
LocalDate startDate = LocalDate.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))).minusDays(DEFAULT_NUMBER_OF_DAYS);
JobDataMap jobDataMap = context.getMergedJobDataMap();
String parameter = ((String) jobDataMap.get("parameter"));
if (parameter != null) {
try {
JSONObject jsonObject = new JSONObject(parameter);
if (!jsonObject.isNull("startDate")) {
String startDateStr = jsonObject.getString("startDate");
startDate = LocalDate.parse(startDateStr);
}
if (!jsonObject.isNull("endDate")) {
String endDateStr = jsonObject.getString("endDate");
endDate = LocalDate.parse(endDateStr);
}
} catch (JSONException e) {
log.error("Error while parsing parameter as JSON, falling back to default parameters.");
}
}
if(!endDate.isAfter(startDate))
throw new RuntimeException("EndDate must be strictly greater than StartDate !");
// 1 : on doit collecter les commandes entres les 2 dates qui n'ont ni logistic_channel_name, ni invoice_logistic_channel_name
// 2 : on doit ensuite déterminer quelle ligne utilisée
// 2.1 : on doit déterminer quel est le sensitive_attribute avec la priorité la plus elevée dans la commande
// 2.2 : on cherche ensuite la ligne par défaut pour le shop, le pays et l'attribut
// 3 : on rempli le champs invoice_logistic_channel_name dans la commande
// commandes sans logistic channel
List<PlatformOrder> platformOrders = platformOrderService.fetchEmptyLogisticChannelOrders(startDate.toString(), endDate.toString());
log.info("Filling default invoice logistic channel name between ["+startDate+" and "+endDate+"]");
if(!platformOrders.isEmpty()) {
log.info("Number of orders to process: "+platformOrders.size());
List<String> orderIds = platformOrders.stream().map(PlatformOrder::getId).collect(Collectors.toList());
Map<String, List<PlatformOrder>> orderMapByAttribute = new HashMap<>();
List<PlatformOrder> tempPlatformOrders = new ArrayList<>(platformOrders);
for(String orderId: orderIds) {
String attributeId = sensitiveAttributeService.getHighestPriorityAttributeId(orderId);
PlatformOrder orderToAdd = tempPlatformOrders.stream().filter
(
order -> order.getId().equals(orderId))
.findAny()
.orElse(null
);
if(!orderMapByAttribute.containsKey(attributeId)){
orderMapByAttribute.put(attributeId, new ArrayList<>());
orderMapByAttribute.get(attributeId).add(orderToAdd);
}
else {
orderMapByAttribute.get(attributeId).add(orderToAdd);
}
tempPlatformOrders.remove(orderToAdd);
}
System.out.println("Attributes in orders : ");
for(Map.Entry<String, List<PlatformOrder>> entry : orderMapByAttribute.entrySet()) {
System.out.println("attribute : " + entry.getKey());
if(entry.getKey() != null) {
for(PlatformOrder order : entry.getValue()) {
System.out.println(order.getId());
}
}
}
System.gc();
Map<String, Map<String, List<PlatformOrder>>> orderMapByShopAndCountry = new HashMap<>();
for (PlatformOrder platformOrder : platformOrders) {
if (orderMapByShopAndCountry.containsKey(platformOrder.getShopId())) {
if (orderMapByShopAndCountry.get(platformOrder.getShopId()).containsKey(platformOrder.getCountry())) {
orderMapByShopAndCountry.get(platformOrder.getShopId()).get(platformOrder.getCountry()).add(platformOrder);
} else {
orderMapByShopAndCountry.get(platformOrder.getShopId()).put(platformOrder.getCountry(), new ArrayList<>());
orderMapByShopAndCountry.get(platformOrder.getShopId()).get(platformOrder.getCountry()).add(platformOrder);
}
} else {
orderMapByShopAndCountry.put(platformOrder.getShopId(), new HashMap<>());
orderMapByShopAndCountry.get(platformOrder.getShopId()).put(platformOrder.getCountry(), new ArrayList<>());
orderMapByShopAndCountry.get(platformOrder.getShopId()).get(platformOrder.getCountry()).add(platformOrder);
}
}
System.gc();
List<String> countries = platformOrders.stream().map(PlatformOrder::getCountry).distinct().collect(Collectors.toList());
List<Country> countryList = countryService.findIdByEnName(countries);
Map<String, String> countryNameToIdMap = countryList.stream().collect(toMap(Country::getNameEn, Country::getId));
Map<String, String> logisticChannelIdToNameMap = logisticChannelService.listByIdAndZhName().stream().collect(toMap(LogisticChannel::getId, LogisticChannel::getZhName));
Map<String, Integer> attributeIdToPriorityMap = sensitiveAttributeService.listIdAndPriority().stream().collect(toMap(SensitiveAttribute::getId, SensitiveAttribute::getPriority));
Map<String, Integer> sortedAttributeIdToPriorityMap = attributeIdToPriorityMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new
));
ListIterator<Map.Entry<String, Integer>> attributeMapIterator = new LinkedList(sortedAttributeIdToPriorityMap.entrySet()).listIterator();
List<LogisticChannelChoice> logisticChannelChoiceList = logisticChannelChoiceService.fetchByShopId(platformOrders.stream().map(PlatformOrder::getShopId).collect(Collectors.toList()));
List<PlatformOrder> ordersToUpdate = new ArrayList<>();
List<String> logisticChoiceErrorList = new ArrayList<>();
for( Map.Entry<String, Map<String, List<PlatformOrder>>> entry: orderMapByShopAndCountry.entrySet()) {
String shopId = entry.getKey();
System.out.println("\nShop => " + shopId);
Map<String, List<PlatformOrder>> orderMapByCountry = entry.getValue();
for(Map.Entry<String, List<PlatformOrder>> countryMapEntry: orderMapByCountry.entrySet()) {
String countryName = countryMapEntry.getKey();
List<PlatformOrder> orders = countryMapEntry.getValue();
System.out.println("---- Country : " + countryName + ", Order number : " + orders.size());
for(PlatformOrder order: orders) {
// reset iterator
attributeMapIterator = new LinkedList(sortedAttributeIdToPriorityMap.entrySet()).listIterator();
// for(Map.Entry<String, Integer> attributeEntry: sortedAttributeIdToPriorityMap.entrySet()) {
String orderAttributeId = sensitiveAttributeService.getHighestPriorityAttributeId(order.getId());
Integer orderAttributePriority = sortedAttributeIdToPriorityMap.get(orderAttributeId);
if(orderMapByAttribute.get(orderAttributeId) == null || orderAttributeId == null) {
continue;
}
List<LogisticChannelChoice> choices = logisticChannelChoiceList.stream().filter(
c -> c.getShopId().equals(shopId) && c.getCountryId().equals(countryNameToIdMap.get(countryName)) && c.getSensitiveAttributeId().equals(orderAttributeId))
.collect(Collectors.toList());
if(choices.isEmpty()) {
while(attributeMapIterator.hasNext()) {
if(attributeMapIterator.next().getKey().equals(orderAttributeId)) {
break;
}
}
// on cherche la ligne avec une priorité plus élevée
if(attributeMapIterator.hasNext()) {
LogisticChannelChoice choice = getHigherLogisticChannelChoice(shopId, countryName, orderAttributePriority, logisticChannelChoiceList, attributeMapIterator, countryNameToIdMap);
if(choice != null){
PlatformOrder orderToAdd = new PlatformOrder();
orderToAdd.setId(order.getId());
orderToAdd.setInvoiceLogisticChannelName(logisticChannelIdToNameMap.get(choice.getLogisticChannelId()));
ordersToUpdate.add(orderToAdd);
System.out.println("La ligne " + choice.getLogisticChannelId() + " a été attribué à commande : " + order.getId());
break;
}
//reset search to prepare for lower priority search
attributeMapIterator = new LinkedList(sortedAttributeIdToPriorityMap.entrySet()).listIterator();
while(attributeMapIterator.hasNext()) {
if(attributeMapIterator.next().getKey().equals(orderAttributeId)) {
break;
}
}
}
// on se rabat sur une ligne avec une priorité plus faible
if(attributeMapIterator.hasPrevious()){
LogisticChannelChoice choice = getLowerLogisticChannelChoice(shopId, countryName, orderAttributePriority, logisticChannelChoiceList, attributeMapIterator, countryNameToIdMap);
if(choice != null){
PlatformOrder orderToAdd = new PlatformOrder();
orderToAdd.setId(order.getId());
orderToAdd.setInvoiceLogisticChannelName(logisticChannelIdToNameMap.get(choice.getLogisticChannelId()));
System.out.println("La ligne " + choice.getLogisticChannelId() + " a été attribué à commande : " + order.getId());
ordersToUpdate.add(orderToAdd);
break;
}
}
// throw new JobExecutionException("No logistic channel choice found for shop : " + shopId + ", country : " + countryName + ", attribute : " + orderAttributeId);
//Send email to admin
log.info("No logistic channel choice found for shop : " + shopId + ", country : " + countryName + ", attribute : " + orderAttributeId);
logisticChoiceErrorList.add("No logistic channel choice found for shop : " + shopId + ", country : " + countryName + ", attribute : " + orderAttributeId);
}
else {
System.out.println("Trouvé");
PlatformOrder orderToAdd = new PlatformOrder();
orderToAdd.setId(order.getId());
orderToAdd.setInvoiceLogisticChannelName(logisticChannelIdToNameMap.get(choices.get(0).getLogisticChannelId()));
ordersToUpdate.add(orderToAdd);
System.out.println("La ligne " + choices.get(0).getLogisticChannelId() + " a été attribué à commande : " + order.getId());
continue;
}
} // end for orders
} // end for in orderMapByCountry
} // end for orderMapByShopAndCountry
System.out.println("Orders to Update => ");
for(PlatformOrder order : ordersToUpdate) {
System.out.println(order.getId());
}
platformOrderService.updateBatchById(ordersToUpdate);
if(!logisticChoiceErrorList.isEmpty()) {
String subject = "Invoice logistic channel choice error";
String destEmail = env.getProperty("spring.mail.username");
Properties prop = emailService.getMailSender();
Map<String, Object> templateModel = new HashMap<>();
templateModel.put("errors", logisticChoiceErrorList);
Session session = Session.getInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(env.getProperty("spring.mail.username"), env.getProperty("spring.mail.password"));
}
});
try {
freemarkerConfigurer = emailService.freemarkerClassLoaderConfig();
Template freemarkerTemplate = freemarkerConfigurer.getConfiguration()
.getTemplate("logisticChannelChoiceError.ftl");
String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);
emailService.sendSimpleMessage(destEmail, subject, htmlBody, session);
log.info("Mail sent successfully");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private LogisticChannelChoice getHigherLogisticChannelChoice(String shopId, String countryName, Integer priority,
List<LogisticChannelChoice> logisticChannelChoiceList, ListIterator<Map.Entry<String, Integer>> attributeMapIterator,
Map<String, String> countryNameToIdMap) throws JobExecutionException {
System.out.println("On se rabat sur une priorité plus élevée");
Map.Entry<String, Integer> nextEntry = attributeMapIterator.next();
System.out.println(nextEntry.getValue() + " ? " + priority);
System.out.println(nextEntry.getKey());
List<LogisticChannelChoice> logisticChannelChoices;
LogisticChannelChoice logisticChannelChoice;
if(nextEntry.getValue() > priority) {
priority = nextEntry.getValue();
String attributeId = nextEntry.getKey();
logisticChannelChoices = logisticChannelChoiceList.stream().filter(choice -> choice.getShopId().equals(shopId) && choice.getCountryId().equals(countryNameToIdMap.get(countryName)) && choice.getSensitiveAttributeId().equals(attributeId)).collect(Collectors.toList());
logisticChannelChoice = logisticChannelChoices.isEmpty() ? null : logisticChannelChoices.get(0);
while(logisticChannelChoice == null && attributeMapIterator.hasNext()) {
logisticChannelChoice = getHigherLogisticChannelChoice(shopId, countryName, priority, logisticChannelChoiceList, attributeMapIterator, countryNameToIdMap);
}
}
else {
throw new JobExecutionException("Sort error");
}
return logisticChannelChoice;
}
private LogisticChannelChoice getLowerLogisticChannelChoice(String shopId, String countryName, Integer priority,
List<LogisticChannelChoice> logisticChannelChoiceList, ListIterator<Map.Entry<String, Integer>> attributeMapIterator,
Map<String, String> countryNameToIdMap) throws JobExecutionException {
System.out.println("On se rabat sur une priorité plus faible");
Map.Entry<String, Integer> previousEntry = attributeMapIterator.previous();
System.out.println(previousEntry.getValue() + " ? " + priority);
System.out.println(previousEntry.getKey());
List<LogisticChannelChoice> logisticChannelChoices;
LogisticChannelChoice logisticChannelChoice;
if(previousEntry.getValue() <= priority) {
priority = previousEntry.getValue();
String attributeId = previousEntry.getKey();
logisticChannelChoices = logisticChannelChoiceList.stream().filter(choice -> choice.getShopId().equals(shopId) && choice.getCountryId().equals(countryNameToIdMap.get(countryName)) && choice.getSensitiveAttributeId().equals(attributeId)).collect(Collectors.toList());
logisticChannelChoice = logisticChannelChoices.isEmpty() ? null : logisticChannelChoices.get(0);
while(logisticChannelChoice == null && attributeMapIterator.hasPrevious()) {
logisticChannelChoice = getLowerLogisticChannelChoice(shopId, countryName, priority, logisticChannelChoiceList, attributeMapIterator, countryNameToIdMap);
}
}
else {
throw new JobExecutionException("Sort error");
}
return logisticChannelChoice;
}
}

View File

@ -66,6 +66,7 @@ public class ShippingInvoice extends AbstractInvoice<String, Object, Integer, Ob
BigDecimal countryPickingFeesPerSKU = BigDecimal.ZERO;
BigDecimal countryPackageMatFeePerOrder = BigDecimal.ZERO;
for (PlatformOrder po : orders) {
System.out.println(po.getId());
countryFretFees = countryFretFees.add(po.getFretFee());
countryServiceFeesPerOrder = countryServiceFeesPerOrder.add(po.getOrderServiceFee());
countryPickingFeesPerOrder = countryPickingFeesPerOrder.add(po.getPickingFee());

View File

@ -484,7 +484,7 @@ public class ShippingInvoiceFactory {
}
}
private void calculateFees(Map<String, LogisticChannel> logisticChannelMap, Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent,
private Map<String, List<String>> calculateFees(Map<String, LogisticChannel> logisticChannelMap, Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent,
Map<LogisticChannel, List<LogisticChannelPrice>> channelPriceMap,
List<Country> countryList,
Map<String, BigDecimal> skuRealWeights,
@ -495,10 +495,11 @@ public class ShippingInvoiceFactory {
Map<String, BigDecimal> shopPackageMatFeeMap,
String invoiceCode
) throws UserException {
Map<String, List<String>> platformOrderIdsWithPb = new HashMap<>();
// find logistic channel price for each order based on its content
for (PlatformOrder uninvoicedOrder : orderAndContent.keySet()) {
List<PlatformOrderContent> contents = orderAndContent.get(uninvoicedOrder);
if (contents.size() == 0) {
if (contents.isEmpty()) {
throw new UserException("Order: {} doesn't have content", uninvoicedOrder.getPlatformOrderId());
}
log.info("Calculating price for {} of order {}", contents, uninvoicedOrder);
@ -513,12 +514,24 @@ public class ShippingInvoiceFactory {
}
// calculate weight of an order
BigDecimal contentWeight = platformOrderContentService.calculateWeight(
Pair<BigDecimal, List<String>> contentWeightResult = platformOrderContentService.calculateWeight(
contentMap,
skuRealWeights
);
Pair<LogisticChannel, LogisticChannelPrice> logisticChannelPair = findAppropriatePrice(countryList, logisticChannelMap,
channelPriceMap, uninvoicedOrder, contentWeight);
if(!contentWeightResult.getValue().isEmpty()) {
platformOrderIdsWithPb.put(uninvoicedOrder.getPlatformOrderId(), contentWeightResult.getValue());
continue;
}
BigDecimal contentWeight = contentWeightResult.getKey();
Pair<LogisticChannel, LogisticChannelPrice> logisticChannelPair;
try {
logisticChannelPair = findAppropriatePrice(countryList, logisticChannelMap,
channelPriceMap, uninvoicedOrder, contentWeight);
}
catch (UserException e) {
platformOrderIdsWithPb.put(uninvoicedOrder.getPlatformOrderId(), Collections.singletonList(e.getMessage()));
continue;
}
LogisticChannelPrice price = logisticChannelPair.getRight();
// update attributes of orders and theirs content
BigDecimal packageMatFee = shopPackageMatFeeMap.get(uninvoicedOrder.getShopId());
@ -557,6 +570,7 @@ public class ShippingInvoiceFactory {
vatApplicable, pickingFeePerItem, content, remainingShippingFee);
}
}
return platformOrderIdsWithPb;
}
private void updateOrdersAndContentsInDb(Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent) {
@ -710,6 +724,8 @@ public class ShippingInvoiceFactory {
+ ", delivered at " + uninvoicedOrder.getShippingTime().toString();
log.error(msg);
throw new UserException(msg);
} catch (UserException e) {
throw new RuntimeException(e);
}
return Pair.of(channel, price);
}
@ -782,18 +798,22 @@ public class ShippingInvoiceFactory {
shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee());
Map<PlatformOrder, List<PlatformOrderContent>> orders = uninvoicedOrdersByShopId.get(shop.getId());
try {
calculateFees(logisticChannelMap, orders, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
Map<String, List<String>> orderIdErrorMap = calculateFees(logisticChannelMap, orders, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
latestDeclaredValues, client, shopServiceFeeMap,shopPackageMatFeeMap, null);
if(!orderIdErrorMap.isEmpty()) {
Map.Entry<String, List<String>> errorEntry = orderIdErrorMap.entrySet().iterator().next();
throw new UserException(errorEntry.getValue().toString());
}
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
ShippingInvoice invoice = new ShippingInvoice(client, "", "", orders, null, eurToUsd);
// Calculate total amounts
invoice.tableData();
estimations.add(new ShippingFeesEstimation(
client.getInternalCode(), shop.getErpCode(), 0, orders.entrySet().size(), invoice.getTotalAmount(), client.getIsCompleteInvoice(), ""));
client.getInternalCode(), shop.getErpCode(), 0, orders.entrySet().size(), invoice.getTotalAmount(), client.getIsCompleteInvoice(), "", new ArrayList<>()));
} catch (UserException e) {
log.error("Couldn't calculate all fees for shop {} for following reason {}", shop.getErpCode(), e.getMessage());
estimations.add(new ShippingFeesEstimation(
client.getInternalCode(), shop.getErpCode(), 0, orders.entrySet().size(), BigDecimal.ZERO, client.getIsCompleteInvoice(), e.getMessage()));
client.getInternalCode(), shop.getErpCode(), 0, orders.entrySet().size(), BigDecimal.ZERO, client.getIsCompleteInvoice(), e.getMessage(), new ArrayList<>()));
errorMessages.add(e.getMessage());
}
}
@ -840,14 +860,31 @@ public class ShippingInvoiceFactory {
shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee());
Map<PlatformOrder, List<PlatformOrderContent>> orders = uninvoicedOrdersByShopId.get(shop.getId());
try {
calculateFees(logisticChannelMap, orders, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
Map<PlatformOrder, List<PlatformOrderContent>> ordersCopy = new HashMap<>(orders);
Map<String, List<String>> platformOrderIdErrorMap = calculateFees(logisticChannelMap, orders, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
latestDeclaredValues, client, shopServiceFeeMap, shopPackageMatFeeMap, null);
System.out.println("Error List : " + platformOrderIdErrorMap);
for(Map.Entry<PlatformOrder,List<PlatformOrderContent>> entry : orders.entrySet()) {
for(Map.Entry<String, List<String>> errorEntry: platformOrderIdErrorMap.entrySet()) {
if(entry.getKey().getPlatformOrderId().equals(errorEntry.getKey())) {
errorMessages.addAll(errorEntry.getValue());
System.out.println("Error List size before : " + platformOrderIdErrorMap.size());
System.out.println("Platform Order Id to remove : " + errorEntry.getKey());
ordersCopy.remove(entry.getKey());
platformOrderIdErrorMap.remove(errorEntry.getKey());
System.out.println("Error List size after : " + platformOrderIdErrorMap.size());
break;
}
}
}
orders = ordersCopy;
List<String> estimationsOrderIds = orders.keySet().stream().map(PlatformOrder::getId).collect(Collectors.toList());
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
ShippingInvoice invoice = new ShippingInvoice(client, "", "", orders, null, eurToUsd);
// Calculate total amounts
invoice.tableData();
estimations.add(new ShippingFeesEstimation(
client.getInternalCode(), shop.getErpCode(), 0, orders.entrySet().size(), invoice.getTotalAmount(), client.getIsCompleteInvoice(), ""));
client.getInternalCode(), shop.getErpCode(), 0, orders.entrySet().size(), invoice.getTotalAmount(), client.getIsCompleteInvoice(), "", estimationsOrderIds));
} catch (UserException e) {
log.error("Couldn't calculate all fees for shop {} for following reason {}", shop.getErpCode(), e.getMessage());
errorMessages.add(e.getMessage());

View File

@ -1,20 +1,66 @@
package org.jeecg.modules.business.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description:
* @Author: jeecg-boot
* @Date: 2023-10-06
* @Version: V1.0
*/
@Data
public class Country {
private final String id;
private final String nameEn;
private final String nameZh;
private final String code;
private final String specialName;
@TableName("country")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="country对象", description="国家")
public class Country implements Serializable {
private static final long serialVersionUID = 1L;
public Country(String id, String nameEn, String nameZh, String code, String specialName) {
this.id = id;
this.nameEn = nameEn;
this.nameZh = nameZh;
this.code = code;
this.specialName = specialName;
}
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "主键")
private java.lang.String id;
/**创建人*/
@ApiModelProperty(value = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建日期")
private java.util.Date createTime;
/**更新人*/
@ApiModelProperty(value = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "更新日期")
private java.util.Date updateTime;
/**英语全名*/
@Excel(name = "英语全名", width = 15)
@ApiModelProperty(value = "英语全名")
private java.lang.String nameEn;
/**中文全名*/
@Excel(name = "中文全名", width = 15)
@ApiModelProperty(value = "中文全名")
private java.lang.String nameZh;
/**ISO 3166 代码*/
@Excel(name = "ISO 3166 代码", width = 15)
@ApiModelProperty(value = "ISO 3166 代码")
private java.lang.String code;
/**特殊名称(匹配马帮用)*/
@Excel(name = "特殊名称(匹配马帮用)", width = 15)
@ApiModelProperty(value = "特殊名称(匹配马帮用)")
private java.lang.String specialName;
}

View File

@ -0,0 +1,81 @@
package org.jeecg.modules.business.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
@Data
@ApiModel(value = "default_logistic_channel", description = "default logistic channel")
@TableName("default_logistic_channel")
public class LogisticChannelChoice implements Serializable
{
private static final long serialVersionUID = 1L;
/**
*
*/
@TableId(type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "主键")
private String id;
/**
*
*/
@ApiModelProperty(value = "创建人")
private String createBy;
/**
*
*/
@JsonFormat(timezone = "GMT+2", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建日期")
private Date createTime;
/**
*
*/
@ApiModelProperty(value = "更新人")
private String updateBy;
/**
*
*/
@JsonFormat(timezone = "GMT+2", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "更新日期")
private Date updateTime;
/**
* ID
*/
@Excel(name = "店铺ID", width = 15, dictTable = "shop", dicText = "erp_code", dicCode = "id")
@Dict(dictTable = "shop", dicText = "erp_code", dicCode = "id")
@ApiModelProperty(value = "店铺ID")
private String shopId;
/**
* ID
*/
@Excel(name = "物流渠道ID", width = 15, dictTable = "logistic_channel", dicText = "zh_name", dicCode = "id")
@Dict(dictTable = "logistic_channel", dicText = "zh_name", dicCode = "id")
@ApiModelProperty(value = "物流渠道ID")
private String logisticChannelId;
/**
* ID
*/
@Excel(name = "订单收件人国家ID", width = 15, dictTable = "country", dicText = "code", dicCode = "id")
@Dict(dictTable = "country", dicText = "code", dicCode = "id")
@ApiModelProperty(value = "订单收件人国家ID")
private String countryId;
/**
* sensitive attribute id
*/
@Excel(name = "sensitive attribute id", width = 15, dictTable = "sensitive_attribute", dicText = "zh_name", dicCode = "id")
@Dict(dictTable = "sensitive_attribute", dicText = "sensitive_attribute", dicCode = "id")
@ApiModelProperty(value = "sensitive attribute id")
private String sensitiveAttributeId;
}

View File

@ -0,0 +1,67 @@
package org.jeecg.modules.business.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: sensitive_attribute
* @Author: jeecg-boot
* @Date: 2023-10-03
* @Version: V1.0
*/
@Data
@TableName("sensitive_attribute")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="sensitive_attribute对象", description="sensitive_attribute")
public class SensitiveAttribute implements Serializable {
private static final long serialVersionUID = 1L;
/**id*/
@TableId(type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "id")
private java.lang.String id;
/**中文描述*/
@Excel(name = "中文描述", width = 15)
@ApiModelProperty(value = "中文描述")
private java.lang.String zhName;
/**英文描述*/
@Excel(name = "英文描述", width = 15)
@ApiModelProperty(value = "英文描述")
private java.lang.String enName;
/**创建人*/
@ApiModelProperty(value = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern="yyyy-MM-dd")
@ApiModelProperty(value = "创建日期")
private java.util.Date createTime;
/**更新人*/
@ApiModelProperty(value = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern="yyyy-MM-dd")
@ApiModelProperty(value = "更新日期")
private java.util.Date updateTime;
/**priority*/
@Excel(name = "priority", width = 15)
@ApiModelProperty(value = "priority")
private java.lang.Integer priority;
}

View File

@ -93,6 +93,13 @@ public class ShippingInvoice implements Serializable {
@Excel(name = "已付金额", width = 15)
@ApiModelProperty(value = "已付金额")
private java.math.BigDecimal paidAmount;
/**
* currency ID
* */
@Dict(dictTable = "currency", dicText = "code", dicCode = "id")
@Excel(name = "currencyID", width = 15)
@ApiModelProperty(value = "currency ID")
private java.lang.String currencyId;
public void setID(String id) {
this.id = id;

View File

@ -60,6 +60,7 @@ public class SkuPrice implements Serializable {
*/
@Dict(dictTable = "sku", dicText = "erp_code", dicCode = "id")
@ApiModelProperty(value = "SKU ID")
@Getter
private String skuId;
/**
*

View File

@ -90,6 +90,14 @@ public class Transaction implements Serializable {
@Excel(name = "invoice number", width = 15)
@ApiModelProperty(value = "invoice number")
private java.lang.String invoiceNumber;
/**shipping fee*/
@Excel(name = "shipping fee", width = 15)
@ApiModelProperty(value = "shippingFee")
private java.math.BigDecimal shippingFee;
/**purchase fee*/
@Excel(name = "purchase fee", width = 15)
@ApiModelProperty(value = "purchaseFee")
private java.math.BigDecimal purchaseFee;
/**amount*/
@Excel(name = "amount", width = 15)
@ApiModelProperty(value = "amount")

View File

@ -6,6 +6,8 @@ import org.jeecg.modules.business.entity.Client;
import org.jeecg.modules.business.entity.UserClient;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Description:
* @Author: jeecg-boot
@ -21,4 +23,10 @@ public interface ClientUserMapper extends BaseMapper<UserClient> {
* @return client entity or null in case that current user is not a client
*/
Client selectClientByUserId(@Param("userId") String userId);
/**
* List all clients registered in wia_app
* @return list of clients
*/
List<Client> listClients();
}

View File

@ -1,6 +1,7 @@
package org.jeecg.modules.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.business.entity.Country;
import org.jeecg.modules.business.vo.CountryCodeAlias;
import org.springframework.stereotype.Repository;
@ -27,6 +28,7 @@ public interface CountryMapper extends BaseMapper<Country> {
* @return list of countries
*/
Country findByEnName(String en_name);
List<Country> findIdByEnName(@Param("countries") List<String> countries);
/**
* Find countries by their chinese name.

View File

@ -0,0 +1,19 @@
package org.jeecg.modules.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.business.entity.LogisticChannelChoice;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Description: logistic channel choice
* @Author: jeecg-boot
* @Date: 2023-10-03
* @Version: V1.0
*/
@Repository
public interface LogisticChannelChoiceMapper extends BaseMapper<LogisticChannelChoice> {
List<LogisticChannelChoice> fetchByShopId(@Param("shopIds") List<String> shopIds);
}

View File

@ -18,4 +18,5 @@ public interface LogisticChannelMapper extends BaseMapper<LogisticChannel> {
List<LogisticChannel> getAll();
List<LogisticChannel> listByIdAndZhName();
}

View File

@ -5,11 +5,13 @@ import io.swagger.models.auth.In;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.business.entity.ClientPlatformOrderContent;
import org.jeecg.modules.business.entity.PlatformOrderContent;
import org.jeecg.modules.business.entity.SkuPrice;
import org.jeecg.modules.business.vo.SkuDetail;
import org.jeecg.modules.business.vo.SkuQuantity;
import org.jeecg.modules.business.vo.SkuWeightDiscountServiceFees;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@ -76,4 +78,7 @@ public interface PlatformOrderContentMapper extends BaseMapper<PlatformOrderCont
void insertPlatformOrderContentsArchives(@Param("orderContents") List<PlatformOrderContent> platformOrderContents);
void cancelInvoice(@Param("invoiceNumber") String invoiceNumber);
void cancelBatchInvoice(@Param("invoiceNumbers") List<String> invoiceNumbers);
List<SkuPrice> searchSkuPrice(@Param("skuIds") List<String> skuIds);
void fetchHighestPriorityAttribute(PlatformOrderContent content);
}

View File

@ -188,4 +188,6 @@ public interface PlatformOrderMapper extends BaseMapper<PlatformOrder> {
void cancelBatchInvoice(@Param("invoiceNumbers") List<String> invoiceNumbers);
List<PlatformOrder> findUninvoicedOrdersByShopForClient(@Param("shopIds") List<String> shopIds, @Param("erpStatuses") List<Integer> erpStatuses);
List<PlatformOrder> fetchEmptyLogisticChannelOrders(@Param("startDate") String startDate,@Param("endDate") String endDate);
}

View File

@ -0,0 +1,22 @@
package org.jeecg.modules.business.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.business.entity.SensitiveAttribute;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
/**
* @Description: sensitive_attribute
* @Author: jeecg-boot
* @Date: 2023-10-03
* @Version: V1.0
*/
@Repository
public interface SensitiveAttributeMapper extends BaseMapper<SensitiveAttribute> {
SensitiveAttribute getHighestPriorityAttribute(@Param("orderId") String orderId);
String getHighestPriorityAttributeId(@Param("orderId") String orderId);
List<SensitiveAttribute> listIdAndPriority();
}

View File

@ -12,4 +12,5 @@ public interface TransactionMapper extends BaseMapper<Transaction> {
List<Transaction> list();
List<Transaction> listByClientId(@Param("clientId") String clientId);
List<Transaction> listByClientIdAndCurrency(@Param("clientId") String clientId, @Param("currency") String currency);
}

View File

@ -5,8 +5,14 @@
resultType="org.jeecg.modules.business.entity.Client">
SELECT c.*
FROM user_client
JOIN client c
ON c.id = user_client.client_id
JOIN client c
ON c.id = user_client.client_id
WHERE user_id = #{userId}
</select>
<select id="listClients" resultType="org.jeecg.modules.business.entity.Client">
SELECT c.*
FROM user_client
JOIN client c
ON c.id = user_client.client_id
</select>
</mapper>

View File

@ -13,6 +13,29 @@
WHERE name_en = #{param1} or special_name = #{param1}
</select>
<select id="findIdByEnName" resultType="org.jeecg.modules.business.entity.Country">
SELECT id,
CASE
WHEN c.name_en IN
<foreach collection="countries" separator="," open="(" close=")" item="country">
#{country}
</foreach> THEN c.name_en
WHEN c.special_name IN
<foreach collection="countries" separator="," open="(" close=")" item="country">
#{country}
</foreach> THEN c.special_name
END as name_en
FROM country as c
WHERE c.name_en IN
<foreach collection="countries" separator="," open="(" close=")" item="country">
#{country}
</foreach>
OR c.special_name IN
<foreach collection="countries" separator="," open="(" close=")" item="country">
#{country}
</foreach>
</select>
<select id="findByZhName" resultType="org.jeecg.modules.business.entity.Country">
SELECT *
FROM country

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.business.mapper.LogisticChannelChoiceMapper">
<select id="fetchByShopId" resultType="org.jeecg.modules.business.entity.LogisticChannelChoice">
SELECT * FROM logistic_channel_choice
WHERE shop_id IN
<foreach collection="shopIds" separator="," open="(" close=")" item="shopId">
#{shopId}
</foreach>;
</select>
<update id="updateLogisticChannelBetweenDate">
UPDATE platform_order
SET update_time = NOW(),
invoice_logistic_channel_name = CASE shop_id
<foreach collection="products" separator=" " open="" close="" index="index" item="item">
WHEN #{item.shop_id} THEN #{item.weight}
</foreach>
END
WHERE invoice_logistic_channel_name IS NULL
</update>
</mapper>

View File

@ -2,5 +2,11 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.business.mapper.LogisticChannelMapper">
<select id="getAll" resultType="org.jeecg.modules.business.entity.LogisticChannel">
SELECT * FROM logistic_channel </select>
SELECT * FROM logistic_channel
</select>
<select id="listByIdAndZhName" resultType="org.jeecg.modules.business.entity.LogisticChannel">
SELECT id, zh_name
FROM logistic_channel;
</select>
</mapper>

View File

@ -327,4 +327,11 @@
#{invoiceNumber}
</foreach>);
</update>
<select id="searchSkuPrice" resultType="org.jeecg.modules.business.entity.SkuPrice">
SELECT * FROM sku_price
WHERE sku_id IN
<foreach collection="skuIds" separator="," open="(" close=")" index="index" item="skuId">
#{skuId}
</foreach>;
</select>
</mapper>

View File

@ -646,4 +646,11 @@
#{invoiceNumber}
</foreach>;
</update>
<select id="fetchEmptyLogisticChannelOrders" resultType="org.jeecg.modules.business.entity.PlatformOrder">
SELECT id, shop_id, country
FROM platform_order po
WHERE logistic_channel_name = ''
AND invoice_logistic_channel_name IS NULL
AND create_time BETWEEN #{startDate} AND #{endDate};
</select>
</mapper>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.business.mapper.SensitiveAttributeMapper">
<select id="getHighestPriorityAttribute" resultType="org.jeecg.modules.business.entity.SensitiveAttribute">
SELECT sa.en_name, sa.priority
FROM sensitive_attribute sa
JOIN product p
ON sa.id = p.sensitive_attribute_id
JOIN sku s
ON p.id = s.product_id
JOIN platform_order_content poc
ON s.id = poc.sku_id
JOIN platform_order po
ON poc.platform_order_id = po.id
WHERE po.id = #{orderId}
ORDER BY sa.priority DESC LIMIT 1;
</select>
<select id="getHighestPriorityAttributeId" resultType="java.lang.String">
SELECT sa.id
FROM sensitive_attribute sa
JOIN product p
ON sa.id = p.sensitive_attribute_id
JOIN sku s
ON p.id = s.product_id
JOIN platform_order_content poc
ON s.id = poc.sku_id
JOIN platform_order po
ON poc.platform_order_id = po.id
WHERE po.id = #{orderId}
ORDER BY sa.priority DESC LIMIT 1;
</select>
<select id="listIdAndPriority" resultType="org.jeecg.modules.business.entity.SensitiveAttribute">
SELECT id, priority
FROM wia_app.sensitive_attribute
</select>
</mapper>

View File

@ -10,4 +10,11 @@
WHERE client_id = #{clientId}
ORDER BY create_time;
</select>
<select id="listByClientIdAndCurrency" resultType="org.jeecg.modules.business.entity.Transaction">
SELECT *
FROM transaction
WHERE client_id = #{clientId}
AND currency = #{currency}
ORDER BY create_time;
</select>
</mapper>

View File

@ -16,6 +16,9 @@ public class CountryService {
public Country findByEnName(String en_name) {
return countryMapper.findByEnName(en_name);
}
public List<Country> findIdByEnName(List<String> countries) {
return countryMapper.findIdByEnName(countries);
}
public List<Country> findAll() {
return countryMapper.findAll();

View File

@ -8,7 +8,7 @@ import java.io.IOException;
import java.util.Properties;
public interface EmailService {
public void sendSimpleMessage();
public void sendSimpleMessage(String recipient, String subject, String text, Session session) throws MessagingException;
public void sendMessageWithAttachment(String recipient, String subject, String text, String attachment, Session session) throws MessagingException, IOException;
public Properties getMailSender();
public FreeMarkerConfigurer freemarkerClassLoaderConfig() throws IOException;

View File

@ -0,0 +1,15 @@
package org.jeecg.modules.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.business.entity.LogisticChannelChoice;
import java.util.List;
/**
* @Description: logistic channel choice
* @Author: jeecg-boot
* @Date: 2023-10-03
* @Version: V1.0
*/
public interface ILogisticChannelChoiceService extends IService<LogisticChannelChoice> {
List<LogisticChannelChoice> fetchByShopId(List<String> shopIds);
}

View File

@ -51,5 +51,5 @@ public interface ILogisticChannelService extends IService<LogisticChannel> {
List<CostTrialCalculation> logisticChannelTrial(int weight, int volume, List<String> countryList);
List<LogisticChannel> listByIdAndZhName();
}

View File

@ -1,6 +1,7 @@
package org.jeecg.modules.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.apache.commons.lang3.tuple.Pair;
import org.jeecg.modules.business.controller.UserException;
import org.jeecg.modules.business.entity.PlatformOrderContent;
import org.jeecg.modules.business.vo.SkuQuantity;
@ -25,7 +26,7 @@ public interface IPlatformOrderContentService extends IService<PlatformOrderCont
* @param skuRealWeights All SKU's real weights
* @return weight
*/
BigDecimal calculateWeight(Map<String, Integer> contentMap, Map<String, BigDecimal> skuRealWeights) throws UserException;
Pair<BigDecimal, List<String>> calculateWeight(Map<String, Integer> contentMap, Map<String, BigDecimal> skuRealWeights) throws UserException;
/**
* Retrieve all SKU weights and discounts

View File

@ -184,4 +184,12 @@ public interface IPlatformOrderService extends IService<PlatformOrder> {
* @return list of orders
*/
List<PlatformOrder> findUninvoicedOrdersByShopForClient(List<String> shopIds, List<Integer> erpStatuses);
/**
* Find all order with empty logistic_channel_name and invoice_logistic_channel_name
* @param startDate
* @param endDate
* @return
*/
List<PlatformOrder> fetchEmptyLogisticChannelOrders(String startDate, String endDate);
}

View File

@ -0,0 +1,20 @@
package org.jeecg.modules.business.service;
import org.jeecg.modules.business.entity.SensitiveAttribute;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* @Description: sensitive_attribute
* @Author: jeecg-boot
* @Date: 2023-10-03
* @Version: V1.0
*/
public interface ISensitiveAttributeService extends IService<SensitiveAttribute> {
SensitiveAttribute getHighestPriorityAttribute(String orderId);
String getHighestPriorityAttributeId(String orderId);
List<SensitiveAttribute> listIdAndPriority();
}

View File

@ -4,6 +4,10 @@ import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.business.entity.Client;
import org.jeecg.modules.business.entity.UserClient;
import java.util.List;
public interface IUserClientService extends IService<UserClient> {
Client getClientByUserId(String userId);
List<Client> listClients();
}

View File

@ -36,8 +36,18 @@ public class EmailServiceImpl implements EmailService {
}
@Override
@Transactional
public void sendSimpleMessage() {
public void sendSimpleMessage(String recipient, String subject, String text, Session session) throws MessagingException {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(Objects.requireNonNull(env.getProperty("spring.mail.username"))));
message.setRecipient(Message.RecipientType.TO, InternetAddress.parse(recipient)[0]);
if(!recipient.equals(env.getProperty("spring.mail.username")))
message.setRecipient(Message.RecipientType.CC, InternetAddress.parse(Objects.requireNonNull(env.getProperty("spring.mail.username")))[0]);
message.setSubject(subject);
message.setContent(text, "text/html; charset=utf-8");
Transport.send(message);
}
@Override
@Transactional

View File

@ -0,0 +1,28 @@
package org.jeecg.modules.business.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.business.entity.LogisticChannelChoice;
import org.jeecg.modules.business.mapper.LogisticChannelChoiceMapper;
import org.jeecg.modules.business.service.ILogisticChannelChoiceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Description: logistic channel choice
* @Author: jeecg-boot
* @Date: 2023-10-03
* @Version: V1.0
*/
@Service
public class LogisticChannelChoiceServiceImpl extends ServiceImpl<LogisticChannelChoiceMapper, LogisticChannelChoice> implements ILogisticChannelChoiceService {
@Autowired
private LogisticChannelChoiceMapper logisticChannelChoiceMapper;
@Override
public List<LogisticChannelChoice> fetchByShopId(List<String> shopIds) {
return logisticChannelChoiceMapper.fetchByShopId(shopIds);
}
}

View File

@ -1,6 +1,7 @@
package org.jeecg.modules.business.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.tuple.Pair;
import org.jeecg.modules.business.controller.UserException;
import org.jeecg.modules.business.entity.LogisticChannelPrice;
import org.jeecg.modules.business.entity.PlatformOrder;
@ -80,10 +81,11 @@ public class LogisticChannelPriceServiceImpl extends ServiceImpl<LogisticChannel
String logisticChannelName = order.getInvoiceLogisticChannelName() == null ?
order.getLogisticChannelName() : order.getInvoiceLogisticChannelName();
BigDecimal weight = platformOrderContentService.calculateWeight(
Pair<BigDecimal, List<String>> calculateWeightRes = platformOrderContentService.calculateWeight(
contentMap,
skuRealWeights
);
BigDecimal weight = calculateWeightRes.getKey();
String countryCode = countryService.findByEnName(order.getCountry()).getCode();

View File

@ -109,4 +109,10 @@ public class LogisticChannelServiceImpl extends ServiceImpl<LogisticChannelMappe
.sorted(Comparator.comparing(CostTrialCalculation::getTotalCost))
.collect(Collectors.toList());
}
@Override
public List<LogisticChannel> listByIdAndZhName() {
return logisticChannelMapper.listByIdAndZhName();
}
}

View File

@ -1,7 +1,10 @@
package org.jeecg.modules.business.service.impl;
import com.amazonaws.services.dynamodbv2.xspec.NULL;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.jeecg.modules.business.controller.UserException;
import org.jeecg.modules.business.entity.PlatformOrderContent;
import org.jeecg.modules.business.mapper.PlatformOrderContentMapper;
@ -13,9 +16,7 @@ import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;
@Slf4j
@Service
@ -36,23 +37,23 @@ public class PlatformOrderContentServiceImpl extends ServiceImpl<PlatformOrderCo
}
@Override
public BigDecimal calculateWeight(Map<String, Integer> contentMap,
Map<String, BigDecimal> skuRealWeights) throws UserException {
public Pair<BigDecimal, List<String>> calculateWeight(Map<String, Integer> contentMap,
Map<String, BigDecimal> skuRealWeights)
{
List<String> errorMessages = new ArrayList<>();
List<String> skuIDs = new ArrayList<>(contentMap.keySet());
log.info("skus : " + skuIDs);
try {
BigDecimal total = contentMap.entrySet().stream()
.map(
content ->
(skuRealWeights.get(content.getKey()).multiply(BigDecimal.valueOf(content.getValue())))
).reduce(BigDecimal.ZERO, BigDecimal::add);
log.info("total weight : " + total);
return total;
} catch (NullPointerException e) {
throw new UserException("Can not find weight for one sku in: " + contentMap);
BigDecimal total = BigDecimal.ZERO;
for(Map.Entry<String, Integer> entry: contentMap.entrySet()) {
if(skuRealWeights.get(entry.getKey()) == null) {
errorMessages.add("Can not find weight for one sku in: " + contentMap);
continue;
}
total = total.add(skuRealWeights.get(entry.getKey()).multiply(BigDecimal.valueOf(entry.getValue())));
}
log.info("total weight : " + total);
return new MutablePair<>(total, errorMessages);
}
@Override

View File

@ -398,4 +398,9 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
public List<PlatformOrder> findUninvoicedOrdersByShopForClient(List<String> shopIds, List<Integer> erpStatuses) {
return platformOrderMap.findUninvoicedOrdersByShopForClient(shopIds, erpStatuses);
}
@Override
public List<PlatformOrder> fetchEmptyLogisticChannelOrders(String startDate, String endDate) {
return platformOrderMap.fetchEmptyLogisticChannelOrders(startDate, endDate);
}
}

View File

@ -0,0 +1,38 @@
package org.jeecg.modules.business.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.entity.SensitiveAttribute;
import org.jeecg.modules.business.mapper.SensitiveAttributeMapper;
import org.jeecg.modules.business.service.ISensitiveAttributeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
/**
* @Description: sensitive_attribute
* @Author: jeecg-boot
* @Date: 2023-10-03
* @Version: V1.0
*/
@Slf4j
@Service
public class SensitiveAttributeServiceImpl extends ServiceImpl<SensitiveAttributeMapper, SensitiveAttribute> implements ISensitiveAttributeService {
@Autowired
private SensitiveAttributeMapper sensitiveAttributeMapper;
@Override
public SensitiveAttribute getHighestPriorityAttribute(String orderId) {
return sensitiveAttributeMapper.getHighestPriorityAttribute(orderId);
}
@Override
public String getHighestPriorityAttributeId(String orderId) {
return sensitiveAttributeMapper.getHighestPriorityAttributeId(orderId);
}
@Override
public List<SensitiveAttribute> listIdAndPriority() {
return sensitiveAttributeMapper.listIdAndPriority();
}
}

View File

@ -249,6 +249,7 @@ public class SkuListMabangServiceImpl extends ServiceImpl<SkuListMabangMapper, S
final String electroMagSensitiveAttributeId = skuListMabangMapper.searchSensitiveAttributeId("Electro-magnetic");
final String electricSensitiveAttributeId = skuListMabangMapper.searchSensitiveAttributeId("Electronic/Electric");
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")
@ -289,7 +290,7 @@ public class SkuListMabangServiceImpl extends ServiceImpl<SkuListMabangMapper, S
if (sku.getMagnetic() == 1)
p.setSensitiveAttributeId(electroMagSensitiveAttributeId);
else
p.setSensitiveAttributeId("");
p.setSensitiveAttributeId(normalSensitiveAttributeId);
}
p.setInvoiceName(sku.getNameEN());
if (sku.getSaleRemark() != null && !sku.getSaleRemark().equals("")) {

View File

@ -8,6 +8,8 @@ import org.jeecg.modules.business.service.IUserClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserClientServiceImpl extends ServiceImpl<ClientUserMapper, UserClient> implements IUserClientService {
@Autowired
@ -16,4 +18,9 @@ public class UserClientServiceImpl extends ServiceImpl<ClientUserMapper, UserCl
public Client getClientByUserId(String userId) {
return clientUserMapper.selectClientByUserId(userId);
}
@Override
public List<Client> listClients() {
return clientUserMapper.listClients();
}
}

View File

@ -0,0 +1,28 @@
package org.jeecg.modules.business.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
@Data
public class Estimation {
private BigDecimal shippingFeesEstimation;
private BigDecimal purchaseEstimation;
private BigDecimal totalEstimation;
private String currency;
private List<String> errorMessages;
public Estimation(@JsonProperty("shippingFeesEstimation") BigDecimal shippingFeesEstimation,
@JsonProperty("purchaseEstimation") BigDecimal purchaseEstimation,
@JsonProperty("currency") String currency,
@JsonProperty("errorMessages") List<String> errorMessages) {
this.currency = currency;
this.purchaseEstimation = purchaseEstimation;
this.shippingFeesEstimation = shippingFeesEstimation;
this.errorMessages = errorMessages;
totalEstimation = shippingFeesEstimation.add(purchaseEstimation).setScale(2, RoundingMode.CEILING);
}
}

View File

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class ShippingFeesEstimation {
@ -22,11 +23,14 @@ public class ShippingFeesEstimation {
private String errorMessage;
private List<String> orderIds;
public ShippingFeesEstimation(@JsonProperty("code") String code, @JsonProperty("shop")String shop,
@JsonProperty("ordersToProcess")Integer ordersToProcess, @JsonProperty("processedOrders")Integer processedOrders,
@JsonProperty("dueForProcessedOrders")BigDecimal dueForProcessedOrders,
@JsonProperty("isCompleteInvoice")String isCompleteInvoice,
@JsonProperty(value = "errorMessage")String errorMessage) {
@JsonProperty(value = "errorMessage")String errorMessage,
@JsonProperty("orderIds")List<String> orderIds) {
this.code = code;
this.shop = shop;
this.ordersToProcess = ordersToProcess;
@ -34,5 +38,6 @@ public class ShippingFeesEstimation {
this.dueForProcessedOrders = dueForProcessedOrders;
this.isCompleteInvoice = isCompleteInvoice;
this.errorMessage = errorMessage;
this.orderIds = orderIds;
}
}

View File

@ -0,0 +1,98 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<table align="center" cellpadding="0" cellspacing="0" width="600" bgcolor="#FFF" style="font-family:Arial,Helvetica,sans-serif;text-align:center;table-layout:fixed;font-size: 16px;border: 1px solid #0B49A6">
<tbody>
<tr>
<td width="600" height="90" bgcolor="#0B49A6" valign="top" align="center" style="padding:20px 0;table-layout:fixed">
<a href="http://app.wia-sourcing.com/user/login">
<img src="https://wia-sourcing.com/wp-content/uploads/2022/10/Fichier-24Icons.png" alt="logo" width="360" style="width:100%;max-width:360px;">
</a>
</td>
</tr>
<tr>
<td align="left">
<table width="520" align="center" style="color:#000;">
<tbody>
<tr>
<td style="padding:35px 0;">Cher(s) collègue(s),</td>
</tr>
<tr>
<td style="padding:0 0 35px 0;">Des erreurs se sont produites lors de l'attribution d'une ligne de transport à une ou plusieurs commandes :</td>
</tr>
<tr>
<td style="padding:10px 0;"><b>Erreurs :</b>
<#if errors?size = 0>
No error
</#if>
</td>
</tr>
<#list errors as error>
<tr>
<td style="padding:10px 0;">
Error: ${error}
</td>
</tr>
</#list>
<tr>
<td style="padding:35px 0 5px 0;">Merci dutiliser nos services.</td>
</tr>
<tr>
<td style="padding:5px 0;">Cordialement</td>
</tr>
<tr>
<td style="padding:5px 0 35px 0;">Léquipe WIA Sourcing.</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td align="left" bgcolor="#0B49A6" width="600">
<table align="center" width="520">
<tbody>
<tr>
<td style="font-style: italic;padding: 20px 0;">Ce message a été envoyé automatiquement. Merci de ne pas répondre. Ce message et ainsi que toutes les pièces jointes sont confidentielles.</td>
</tr>
<tr>
<td style="padding: 0 0 20px 0;">Si vous avez reçu ce message par erreur, merci de nous avertir immédiatement et de détruire ce message.</td>
</tr>
<tr>
<td>Service client :</td>
</tr>
<tr>
<td>Pour obtenir plus dinformations concernant nos services, veuillez nous contacter à ladresse ci-dessous ou en visitant notre site web.</td>
</tr>
</tbody>
</table>
<table align="center" width="520" style="padding: 15px 0">
<tbody>
<tr>
<td width="220" style="text-align:center;border-radius:2em;padding:20px 10px 20px 0;;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/contactez-nous" style="color:white;text-decoration:none">Nous contacter</a></td>
<td width="40" ></td>
<td width="220" style="text-align:center;border-radius:2em;padding:20px 0 20px 10px;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/" style="color:white;text-decoration:none">Notre site web</a></td>
</tr>
</tbody>
</table>
<table align="center" width="520" style="padding: 0 0 35px 0;">
<tbody>
<tr>
<td style="color:#EF5A1A;">WIA SOURCING</td>
</tr>
<tr>
<td>© 2018/2023 par WIA Sourcing Agency.</td>
</tr>
<tr>
<td>TOUS DROITS RÉSERVÉS©</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</body>
</html>