feature : Create Mabang Purchase Order

pull/6221/head
Gauthier LO 2024-03-12 16:22:17 +01:00
parent 8253ba1877
commit e5fe9a4989
13 changed files with 216 additions and 46 deletions

View File

@ -14,6 +14,7 @@ import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.business.controller.UserException;
import org.jeecg.modules.business.domain.job.ThrottlingExecutorService;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.service.*;
import org.jeecg.modules.business.vo.*;
@ -37,6 +38,9 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@ -66,27 +70,25 @@ public class PurchaseOrderController {
private IPlatformOrderService platformOrderService;
@Autowired
private IShippingInvoiceService shippingInvoiceService;
@Autowired private IProviderMabangService providerMabangService;
private static final Integer DEFAULT_NUMBER_OF_THREADS = 2;
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 10;
/**
* Page query for purchase order
*
* @param purchaseOrder
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@AutoLog(value = "商品采购订单-分页列表查询")
@ApiOperation(value = "商品采购订单-分页列表查询", notes = "商品采购订单-分页列表查询")
@GetMapping(value = "/list")
public Result<?> queryPageList(PurchaseOrder purchaseOrder,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<PurchaseOrder> queryWrapper = QueryGenerator.initQueryWrapper(purchaseOrder, req.getParameterMap());
Page<PurchaseOrder> page = new Page<>(pageNo, pageSize);
IPage<PurchaseOrder> pageList = purchaseOrderService.page(page, queryWrapper);
return Result.OK(pageList);
public Result<?> queryPageList(@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
Page<PurchaseOrderPage> page = new Page<>(pageNo, pageSize);
purchaseOrderService.setPageForList(page);
return Result.OK(page);
}
/**
*
@ -590,4 +592,31 @@ public class PurchaseOrderController {
PurchaseConfirmation d = platformOrderService.confirmPurchaseBySkuQuantity(clientInfo, skuQuantities);
return Result.OK(d);
}
@GetMapping(value = "/createMabangPurchaseOrder")
public Result<?> createMabangPurchaseOrder(@RequestParam("invoiceNumbers") List<String> request) {
log.info("Creating purchase order to Mabang for invoices : {} ", request);
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<CompletableFuture<Boolean>> changeOrderFutures = request.stream()
.map(invoiceNumber -> CompletableFuture.supplyAsync(() -> {
log.info("Invoice number : {}", invoiceNumber);
List<SkuQuantity> skuQuantities = purchaseOrderService.getSkuQuantityByInvoiceNumber(invoiceNumber);
if(skuQuantities.isEmpty()) {
return false;
}
Map<String, Integer> skuQuantityMap = skuQuantities.stream()
.collect(Collectors.toMap(SkuQuantity::getErpCode, SkuQuantity::getQuantity));
skuQuantityMap.forEach((s, integer) -> log.info("SKU: {} Quantity: {}", s, integer));
InvoiceMetaData metaData = purchaseOrderService.getMetaDataFromInvoiceNumbers(invoiceNumber);
return providerMabangService.addPurchaseOrderToMabang(skuQuantityMap, metaData);
},throttlingExecutorService))
.collect(Collectors.toList());
List<Boolean> results = changeOrderFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
long nbSuccesses = results.stream().filter(b -> b).count();
log.info("{}/{} purchase order requests have succeeded.", nbSuccesses, request.size());
return Result.ok("data.noData");
}
}

View File

@ -322,6 +322,7 @@ public class InvoiceController {
/**
* Make purchase invoice for specified orders and type
* used by self-service clients
* creates a purchase order in Mabang
* @param param Parameters for creating an invoice
* @return Result of the generation, in case of error, message will be contained,
* in case of success, data will contain filename.
@ -426,12 +427,11 @@ public class InvoiceController {
List<SkuQuantity> skuQuantities = new ArrayList<>();
for(Map.Entry<String, Integer> entry : payload.entrySet()) {
String skuId = skuService.getIdFromErpCode(entry.getKey());
skuQuantities.add(new SkuQuantity(skuId, entry.getValue()));
skuQuantities.add(new SkuQuantity(skuId, entry.getKey(), entry.getValue()));
}
try {
String purchaseId = purchaseOrderService.addPurchase(skuQuantities);
metaData = purchaseOrderService.makeInvoice(purchaseId);
providerMabangService.addPurchaseOrderToMabang(payload, metaData);
return Result.OK(metaData);
} catch (UserException e) {
return Result.error(e.getMessage());

View File

@ -49,15 +49,15 @@ public class SkuListRawStream implements NetworkDataStream<SkuListResponse> {
if (!began) {
throw new IllegalStateException("Calling hasNext before begin");
}
// still has page left, true
if (!currentResponse.getCursor().isEmpty() || currentResponse.getCursor().equals(toSend.getCursor())) {
log.info("page: {}, has next", toSend.getPage());
toSend.setCursor(currentResponse.getCursor());
return true;
}
// no page left, false
log.info("No page left, end");
return false;
if(currentResponse.getCursor().isEmpty()) {
log.info("No page left, end");
return false;
}
// still has page left, true
log.info("page: {}, has next", toSend.getPage());
toSend.setCursor(currentResponse.getCursor());
return true;
}
/**

View File

@ -103,6 +103,12 @@ public class PurchaseOrder implements Serializable {
@ApiModelProperty(value = "订单发票号")
private String invoiceNumber;
/**
*
*/
@Excel(name = "订单是否下了单", width = 15)
@ApiModelProperty(value = "订单是否下了单")
private boolean ordered;
/**
* Payment document

View File

@ -7,6 +7,9 @@ import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.business.entity.PlatformOrder;
import org.jeecg.modules.business.entity.PurchaseOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.business.vo.InvoiceMetaData;
import org.jeecg.modules.business.vo.PurchaseOrderPage;
import org.jeecg.modules.business.vo.SkuQuantity;
import org.springframework.stereotype.Repository;
/**
@ -105,4 +108,14 @@ public interface PurchaseOrderMapper extends BaseMapper<PurchaseOrder> {
List<PurchaseOrder> getPurchasesByInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
List<PlatformOrder> getPlatformOrder(@Param("invoiceNumber") String invoiceNumber);
List<SkuQuantity> getSkuQuantityByInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
InvoiceMetaData getMetaDataFromInvoiceNumbers(@Param("invoiceNumber") String invoiceNumber);
List<PurchaseOrderPage> getPage(@Param("offset") long offset, @Param("size") long size);
long countPurchaseOrders();
void updatePurchaseOrderStatus(@Param("invoiceNumber") String invoiceNumber, @Param("isOrdered") boolean isOrdered);
}

View File

@ -103,4 +103,40 @@
FROM platform_order
WHERE shipping_invoice_number = #{invoiceNumber}
</select>
<select id="getSkuQuantityByInvoiceNumber" resultType="org.jeecg.modules.business.vo.SkuQuantity">
SELECT pos.sku_id AS ID, sku.erp_code AS erp_code, SUM(pos.quantity) AS quantity
FROM purchase_order po
JOIN purchase_order_sku pos
ON po.id = pos.purchase_order_id
JOIN sku
ON pos.sku_id = sku.id
WHERE po.invoice_number = #{invoiceNumber}
GROUP BY pos.sku_id;
</select>
<select id="getMetaDataFromInvoiceNumbers" resultType="org.jeecg.modules.business.vo.InvoiceMetaData">
SELECT CONCAT('Invoice N°', po.invoice_number, ' (', c.invoice_entity ,')') as filename, po.invoice_number AS invoiceCode, c.invoice_entity AS invoiceEntity, c.internal_code AS internalCode, null as errorMsg
FROM purchase_order po
JOIN client c
ON po.client_id = c.id
WHERE po.invoice_number = #{invoiceNumber}
</select>
<select id="getPage" resultType="org.jeecg.modules.business.vo.PurchaseOrderPage">
SELECT po.*, GROUP_CONCAT(p.platform_order_id) as platformOrderId
FROM purchase_order po
LEFT JOIN platform_order p
ON p.purchase_invoice_number = po.invoice_number
GROUP BY po.id, po.create_time
ORDER BY po.create_time DESC
LIMIT #{size} OFFSET #{offset}
</select>
<select id="countPurchaseOrders" resultType="java.lang.Long">
SELECT COUNT(*)
FROM purchase_order
</select>
<update id="updatePurchaseOrderStatus">
UPDATE purchase_order
SET ordered = #{isOrdered}
WHERE invoice_number = #{invoiceNumber}
</update>
</mapper>

View File

@ -140,13 +140,14 @@
</select>
<select id="getSkuQuantitiesFromOrderIds" resultType="org.jeecg.modules.business.vo.SkuQuantity">
SELECT sku_id as ID, SUM(quantity) AS quantity
SELECT sku_id as ID, sku.erp_code as erp_code, SUM(quantity) AS quantity
FROM platform_order_content
JOIN sku ON platform_order_content.sku_id = sku.id
WHERE platform_order_id IN
<foreach collection="orderIds" separator="," open="(" close=")" index="index" item="orderId">
#{orderId}
</foreach>
AND erp_status IN ('1','2', '3')
AND erp_status IN ('1','2','3')
AND product_available = 0
AND virtual_product_available = 0
GROUP BY sku_id;

View File

@ -15,5 +15,5 @@ public interface IProviderMabangService extends IService<ProviderData> {
*/
void saveProviderFromMabang(List<ProviderData> providerDataList);
void addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData);
boolean addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData);
}

View File

@ -1,10 +1,12 @@
package org.jeecg.modules.business.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.business.controller.UserException;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.vo.InvoiceMetaData;
import org.jeecg.modules.business.vo.PurchaseOrderPage;
import org.jeecg.modules.business.vo.SkuQuantity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -127,4 +129,12 @@ public interface IPurchaseOrderService extends IService<PurchaseOrder> {
List<PurchaseOrder> getPurchasesByInvoiceNumber(String invoiceNumber);
List<PlatformOrder> getPlatformOrder(String invoiceNumber);
List<SkuQuantity> getSkuQuantityByInvoiceNumber(String invoiceNumber);
InvoiceMetaData getMetaDataFromInvoiceNumbers(String invoiceNumber);
void setPageForList(Page<PurchaseOrderPage> page);
void updatePurchaseOrderStatus(String invoiceNumber, boolean isOrdered);
}

View File

@ -10,21 +10,27 @@ import org.jeecg.modules.business.domain.api.mabang.purDoAddPurchase.AddPurchase
import org.jeecg.modules.business.domain.api.mabang.purDoAddPurchase.AddPurchaseOrderResponse;
import org.jeecg.modules.business.domain.api.mabang.purDoAddPurchase.SkuStockData;
import org.jeecg.modules.business.domain.api.mabang.purDoGetProvider.ProviderData;
import org.jeecg.modules.business.domain.api.mabang.purDoGetProvider.ProviderRequestErrorException;
import org.jeecg.modules.business.domain.job.ThrottlingExecutorService;
import org.jeecg.modules.business.entity.Provider;
import org.jeecg.modules.business.mapper.ProviderMabangMapper;
import org.jeecg.modules.business.service.IProviderMabangService;
import org.jeecg.modules.business.service.IProviderService;
import org.jeecg.modules.business.service.IPurchaseOrderService;
import org.jeecg.modules.business.service.ISkuService;
import org.jeecg.modules.business.vo.InvoiceMetaData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@ -39,8 +45,13 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
@Autowired
private IProviderService providerService;
@Autowired
private IPurchaseOrderService purchaseOrderService;
@Autowired
ISkuService skuService;
private static final Integer DEFAULT_NUMBER_OF_THREADS = 1;
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 10;
@Override
public void saveProviderFromMabang(List<ProviderData> providerDataList) {
if(providerDataList.isEmpty()) {
@ -56,15 +67,20 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
}
}
/**
* Add purchase order to Mabang.
* @param skuQuantities Quantities mapped to sku erpCodes
* @param metaData invoice meta data
*/
@Transactional
@Override
public void addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData) {
public boolean addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String mabangUsername = sysUser.getMabangUsername();
String content = metaData.getFilename().substring(0, metaData.getFilename().lastIndexOf("."));
String content = metaData.getFilename();
List<String> stockSkuList = new ArrayList<>();
String stockSkus;
log.info("Creating purchase order to Mabang : {} skus", skuQuantities.size());
log.info("Creating purchase order to Mabang {} : {} skus", metaData.getInvoiceCode(),skuQuantities.size());
Map<String, SkuData> skuDataMap = new HashMap<>();
List<SkuData> skuDataList = new ArrayList<>();
log.info("Requesting SKU data from Mabang API.");
@ -96,7 +112,8 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
skuDataList.addAll(skuListStream.all());
}
if(skuDataList.isEmpty()) {
throw new SkuListRequestErrorException("Couldn't get SKU data from Mabang API.");
log.error("Couldn't get SKU data from Mabang API for invoice : {}", metaData.getInvoiceCode());
return false;
}
for(SkuData skuData : skuDataList) {
@ -106,7 +123,7 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
for(Map.Entry<String, SkuData> entry : skuDataMap.entrySet()) {
SkuStockData stockData = new SkuStockData();
stockData.setStockSku(entry.getKey());
stockData.setPrice(entry.getValue().getPurchasePrice());
stockData.setPrice(entry.getValue().getPurchasePrice().compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ONE : entry.getValue().getPurchasePrice().setScale(2, RoundingMode.CEILING));
stockData.setPurchaseNum(skuQuantities.get(entry.getKey()));
stockData.setProvider(entry.getValue().getProvider());
skuStockData.add(stockData);
@ -122,21 +139,36 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
stockProviderMap.put(stockData.getProvider(), stockDataList);
}
}
log.info("Create {} purchase orders to Mabang.", stockProviderMap.size());
log.info("Creating {} purchase orders to Mabang - {}", stockProviderMap.size(), metaData.getInvoiceCode());
// group id is the response from mabang API
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<String> groupIds = new ArrayList<>();
for(Map.Entry<String, List<SkuStockData>> entry : stockProviderMap.entrySet()) {
String providerName = entry.getKey();
List<SkuStockData> stockDataList = entry.getValue();
AddPurchaseOrderRequestBody body = new AddPurchaseOrderRequestBody(mabangUsername, providerName, content, stockDataList);
AddPurchaseOrderRequest request = new AddPurchaseOrderRequest(body);
AddPurchaseOrderResponse response = request.send();
log.info("Response from Mabang Add purchase API : " + response.toString());
if(!response.success())
throw new ProviderRequestErrorException("Couldn't add purchase order to Mabang.");
groupIds.add(response.getGroupId());
}
log.info("Purchase orders created to Mabang, groupIds : {}", groupIds);
List<CompletableFuture<Boolean>> changeOrderFutures = stockProviderMap.entrySet().stream()
.map(entry -> CompletableFuture.supplyAsync(() -> {
String providerName = entry.getKey();
List<SkuStockData> stockDataList = entry.getValue();
log.info("Creating purchase order to Mabang {} :\n -provider : {}\n-content : {}\n-stockDataList : {}",metaData.getInvoiceCode(), providerName, content , stockDataList);
AddPurchaseOrderRequestBody body = new AddPurchaseOrderRequestBody(mabangUsername, providerName, content, stockDataList);
AddPurchaseOrderRequest request = new AddPurchaseOrderRequest(body);
AddPurchaseOrderResponse response = request.send();
log.info("Mabang Add purchase API response | {} - {} : {}", metaData.getInvoiceCode(), providerName,response.toString());
if(!response.success()) {
log.error("Failed to create purchase order to Mabang for {} - {}", metaData.getInvoiceCode(), providerName);
return false;
}
groupIds.add(response.getGroupId());
return true;
}, throttlingExecutorService))
.collect(Collectors.toList());
List<Boolean> results = changeOrderFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
long nbSuccesses = results.stream().filter(b -> b).count();
log.info("{}/{} purchase orders created successfully on Mabang. {} : GroupIds : {}", nbSuccesses, stockProviderMap.size(), metaData.getInvoiceCode(),groupIds);
// change status of purchase order to 'ordered' = true
purchaseOrderService.updatePurchaseOrderStatus(metaData.getInvoiceCode(), true);
return true;
}
}

View File

@ -1,6 +1,7 @@
package org.jeecg.modules.business.service.impl.purchase;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
@ -718,4 +719,27 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
public List<PlatformOrder> getPlatformOrder(String invoiceNumber) {
return purchaseOrderMapper.getPlatformOrder(invoiceNumber);
}
@Override
public List<SkuQuantity> getSkuQuantityByInvoiceNumber(String invoiceNumber) {
return purchaseOrderMapper.getSkuQuantityByInvoiceNumber(invoiceNumber);
}
@Override
public InvoiceMetaData getMetaDataFromInvoiceNumbers(String invoiceNumber) {
return purchaseOrderMapper.getMetaDataFromInvoiceNumbers(invoiceNumber);
}
@Override
public void setPageForList(Page<PurchaseOrderPage> page) {
System.out.println("Offset: " + page.offset() + ", Size: " + page.getSize());
List<PurchaseOrderPage> purchaseOrderPages = purchaseOrderMapper.getPage(page.offset(), page.getSize());
page.setRecords(purchaseOrderPages);
page.setTotal(purchaseOrderMapper.countPurchaseOrders());
}
@Override
public void updatePurchaseOrderStatus(String invoiceNumber, boolean isOrdered) {
purchaseOrderMapper.updatePurchaseOrderStatus(invoiceNumber, isOrdered);
}
}

View File

@ -68,10 +68,14 @@ public class PurchaseOrderPage {
/**最终金额*/
@ApiModelProperty(value = "最终金额")
private java.math.BigDecimal finalAmount;
/**Purchase status*/
@Excel(name = "status", width = 15)
@ApiModelProperty(value = "status")
private String status;
/**paid amount*/
@Excel(name = "已付金额", width = 15)
@ApiModelProperty(value = "已付金额")
private java.math.BigDecimal paidAmount;
/**订单是否下了单*/
@Excel(name = "订单是否下了单", width = 15)
@ApiModelProperty(value = "订单是否下了单")
private boolean ordered;
/**
* Payment document

View File

@ -1,12 +1,27 @@
package org.jeecg.modules.business.vo;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
@Data
public class SkuQuantity {
@JSONField(name = "id")
private final String ID;
@JSONField(name = "erpCode")
private final String erpCode;
@JSONField(name = "quantity")
private final Integer quantity;
public SkuQuantity(String ID, String erpCode, Integer quantity){
this.ID = ID;
this.erpCode = erpCode;
this.quantity = quantity;
}
public SkuQuantity(String ID, Integer quantity){
this.ID = ID;
this.quantity = quantity;
this.erpCode = null;
}
@Override
public String toString(){
return String.format("|Sku ID %s -- %d|", ID, quantity);