feat: (WIP) download invoice details over period, MabangSkuStockUpdateJob added clients param, fixes

pull/8040/head
Gauthier LO 2024-12-10 12:33:46 +01:00
parent f8e66e6208
commit 8cf45c1389
22 changed files with 222 additions and 29 deletions

View File

@ -590,20 +590,33 @@ public class PurchaseOrderController {
.filter(entry -> entry.getValue() > 0)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
InvoiceMetaData metaData = purchaseOrderService.getMetaDataFromInvoiceNumbers(invoiceNumber);
boolean success = providerMabangService.addPurchaseOrderToMabang(skuQtyNotEmptyMap, metaData, providersHistory);
return success ? invoiceNumber : "failed";
List<String> errors = providerMabangService.addPurchaseOrderToMabang(skuQtyNotEmptyMap, metaData, providersHistory);
return errors.isEmpty() ? invoiceNumber : errors.toString();
},throttlingExecutorService))
.collect(Collectors.toList());
List<String> results = future.stream().map(CompletableFuture::join).collect(Collectors.toList());
long nbSuccesses = results.stream().filter(Objects::nonNull).count();
long nbSuccesses = results.stream().filter(PurchaseOrderController::isInvoiceNumber).count();
log.info("{}/{} purchase order requests have succeeded.", nbSuccesses, invoiceNumbers.size());
Map<String, List<String>> data = new HashMap<>();
List<String> failedInvoices = results.stream().filter(s -> s.equals("failed")).collect(Collectors.toList());
List<String> successInvoices = results.stream().filter(s -> !s.equals("failed")).collect(Collectors.toList());
List<String> failedInvoices = new ArrayList<>();
List<String> successInvoices = new ArrayList<>();
results.forEach(result -> {
if(isInvoiceNumber(result)) {
successInvoices.add(result);
} else {
failedInvoices.add(result);
}
});
data.put("fail", failedInvoices);
data.put("success", successInvoices);
return Result.OK(data);
}
public static boolean isInvoiceNumber(String invoiceNumber) {
return invoiceNumber.matches("^[0-9]{4}-[0-9]{2}-[127][0-9]{3}$");
}
}

View File

@ -492,6 +492,7 @@ public class SkuController {
@PostMapping("/syncSkuQty")
public Result<?> syncSkuQty(@RequestBody List<String> erpCodes) {
log.info("Syncing sku stock for SKUs : {}", erpCodes);
skuListMabangService.mabangSkuStockUpdate(erpCodes);
return Result.OK();
}

View File

@ -229,6 +229,21 @@ public class InvoiceController {
return Result.OK(period);
else return Result.error("No package in the selected period");
}
/**
* Fetches dates of first invoice and last invoice for given shops (only return earliest and latest)
* @param shopIds
* @return
*/
@GetMapping(value = "/invoicePeriod")
public Result<?> getInvoicePeriod(@RequestParam("shopIds") String shopIds) {
log.info("Request for invoice period for shops: {}", shopIds);
List<String> shopIdList = Arrays.asList(shopIds.split(","));
Period period = iShippingInvoiceService.getInvoicePeriod(shopIdList);
if (period.isValid())
return Result.OK(period);
else return Result.error("No package in the selected period");
}
/**
* Make shipping invoice for shops between 2 dates and orders with specified status.
*
@ -741,6 +756,20 @@ public class InvoiceController {
List<ExtraFeeResult> extraFees = extraFeeService.findByInvoiceNumber(invoiceNumber);
return shippingInvoiceService.exportToExcel(factureDetails, refunds, extraFees, invoiceNumber, invoiceEntity, internalCode);
}
@GetMapping(value = "/downloadInvoiceDetailByClientAndPeriod")
public byte[] downloadInvoiceDetailByClientAndPeriod(@RequestParam("clientId") String clientId,
@RequestParam("shopIds[]")List<String> shopIds,
@RequestParam("startDate") String startDate,
@RequestParam("endDate") String endDate,
@RequestParam("type") String type
) throws IOException, UserException {
// TODO : fix this + fix missing sku_price
System.out.println("Request for downloading invoice detail by client and period : " + clientId + " " + shopIds + " " + startDate + " " + endDate + " " + type);
List<FactureDetail> invoiceDetails = shippingInvoiceService.getInvoiceDetailByShopsAndPeriod(shopIds, startDate, endDate, type);
Client client = clientService.getById(clientId);
return shippingInvoiceService.exportToExcel(invoiceDetails, Collections.emptyList(), Collections.emptyList(), "", client.getInvoiceEntity(), client.getInternalCode());
}
@GetMapping(value = "/downloadInvoiceInventory")
public byte[] downloadInvoiceInventory(@RequestParam("invoiceCode") String invoiceCode, @RequestParam("internalCode") String internalCode, @RequestParam("invoiceEntity") String invoiceEntity) throws IOException {
InvoiceMetaData metaData = new InvoiceMetaData("", invoiceCode, internalCode, invoiceEntity, "");

View File

@ -53,8 +53,8 @@ public class SkuData {
@JSONField(name="purchasePrice")
private BigDecimal purchasePrice;
/**默认供应商名称,接口参数传showProvider才返回*/
@JSONField(name="provider")
private String provider;
@JSONField(name="warehouse")
private String warehouse;
/**
* if stockPicture is empty, we use it
*/
@ -146,7 +146,14 @@ public class SkuData {
"\nStock Picture : " + this.stockPicture +
"\nsale Picture : " + this.salePicture +
"\nBattery : " + this.hasBattery +
"\nMagnetic : " + this.magnetic
"\nMagnetic : " + this.magnetic +
"\nPowder : " + this.powder +
"\nIs Paste : " + this.isPaste +
"\nNo Liquid Cosmetic : " + this.noLiquidCosmetic +
"\nIs Flammable : " + this.isFlammable +
"\nIs Knife : " + this.isKnife +
"\nIs Gift : " + this.isGift +
"\nProvider : " + this.supplier
;
}
}

View File

@ -85,7 +85,7 @@ public class SkuAddRequestBody implements RequestBody {
this.declareValue = data.getDeclareValue();
this.declareName = data.getDeclareNameZh();
this.declareEname = data.getDeclareNameEn();
this.warehouse = data.getProvider();
this.warehouse = data.getWarehouse();
this.remark = data.getSaleRemark();
this.hasBattery = data.getHasBattery();
this.magnetic = data.getMagnetic();

View File

@ -33,7 +33,18 @@ public class MabangSkuStockUpdateJob implements Job {
if (parameter != null) {
try {
JSONObject jsonObject = new JSONObject(parameter);
if (!jsonObject.isNull("skus")) {
if(!jsonObject.isNull("clients")) {
log.info("Request to sync stock for all SKUs of clients: {}", jsonObject.getJSONArray("clients"));
erpCodes.clear();
JSONArray array = jsonObject.getJSONArray("clients");
for(int i = 0; i < array.length(); i++) {
String clientCode = array.getString(i);
erpCodes.addAll(skuService.fetchAllClientSkuCodes(clientCode));
}
}
if (!jsonObject.isNull("skus") && jsonObject.isNull("clients")) {
log.info("Request to sync stock for specific SKUs: {}", jsonObject.getJSONArray("skus"));
erpCodes.clear();
JSONArray array = jsonObject.getJSONArray("skus");
for(int i = 0; i < array.length(); i++) {
erpCodes.add(array.getString(i));
@ -42,6 +53,8 @@ public class MabangSkuStockUpdateJob implements Job {
} catch (JSONException e) {
log.error("Error while parsing parameter as JSON, falling back to default parameters.");
}
} else {
log.info("No parameter provided, syncing stock for all SKUs.");
}
skuListMabangService.mabangSkuStockUpdate(erpCodes);
log.info("Sku stock update Job has ended.");

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.vo.FactureDetail;
import org.springframework.stereotype.Repository;
@ -9,4 +10,5 @@ import java.util.List;
@Repository
public interface FactureDetailMapper extends BaseMapper<FactureDetail> {
List<FactureDetail> selectByShopsAndPeriod(@Param("shopIds") List<String> shopIds, @Param("start") String startDate, @Param("end") String endDate, @Param("type") String type);
}

View File

@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.vo.InvoiceKpi;
import org.jeecg.modules.business.vo.ParcelInfos;
import org.jeecg.modules.business.vo.Period;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
@ -28,4 +29,6 @@ public interface ShippingInvoiceMapper extends BaseMapper<ShippingInvoice> {
Client getClientByInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
List<ParcelInfos> getPackageStatusInPeriod(@Param("period") int period);
Period getInvoicePeriod(@Param("shops") List<String> shopIdList);
}

View File

@ -78,4 +78,6 @@ public interface SkuMapper extends BaseMapper<Sku> {
List<SkuOrderPage> searchExistingSkuByKeywords(@Param("keywords") List<String> keywords);
List<Sku> listImgUrls();
List<String> fetchAllClientSkuCodes(@Param("clientCode") String clientCode);
}

View File

@ -0,0 +1,52 @@
<?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.FactureDetailMapper">
<select id="selectByShopsAndPeriod" resultType="org.jeecg.modules.business.vo.FactureDetail">
SELECT s.name AS 'Boutique',
po.platform_order_id AS 'N° de Mabang',
po.platform_order_number AS 'N° de commande',
po.tracking_number AS 'N° de suivi',
po.order_time AS 'Date de commande',
po.shipping_time AS 'Date d\'expédition',
po.recipient AS 'Nom de client',
po.country AS 'Pays',
po.postcode AS 'Code postal',
JSON_ARRAYAGG(sku.erp_code) AS 'SKU',
JSON_ARRAYAGG(sku.en_name) AS 'Nom produits',
JSON_ARRAYAGG(poc.quantity) AS 'Quantité',
SUM(
IF (poc.purchase_fee = 0,
poc.quantity *
(SELECT price
FROM sku_price sp
WHERE sku_id = poc.sku_id
AND sp.date &lt;= po.order_time
ORDER BY sp.date DESC
LIMIT 1
),
poc.purchase_fee
)
) AS 'Frais d\'achat',
po.fret_fee AS 'Frais de FRET',
SUM(poc.shipping_fee) AS 'Frais de livraison',
po.order_service_fee + SUM(poc.service_fee) AS 'Frais de service',
po.picking_fee + SUM(poc.picking_fee) AS 'Frais de préparation',
po.packaging_material_fee AS 'Frais de matériel d\'emballage',
SUM(poc.vat) AS 'TVA',
po.shipping_invoice_number AS 'N° de facture'
FROM platform_order po
JOIN shop s ON po.shop_id = s.id
RIGHT JOIN platform_order_content poc ON po.id = poc.platform_order_id
JOIN sku ON poc.sku_id = sku.id
WHERE shipping_invoice_number IS NOT NULL
AND poc.erp_status &lt;&gt; 5
AND po.order_time &gt; #{start}
AND po.order_time &lt; #{end}
and po.shop_id IN
<foreach collection="shopIds" item="shopId" open="(" close=")" separator=",">
#{shopId}
</foreach>
GROUP BY po.id, s.name, po.order_time
ORDER BY s.name, po.order_time;
</select>
</mapper>

View File

@ -100,6 +100,16 @@
AND erp_status IN (3,4)
AND parcel_trace.id IN (SELECT id FROM latest_parcel_trace_id)
</select>
<select id="gpt">
<select id="getInvoicePeriod" resultType="org.jeecg.modules.business.vo.Period">
SELECT MIN(po.order_time) as 'start',
MAX(po.order_time) as 'end',
'' as type
FROM platform_order po
JOIN shop s ON po.shop_id = s.id
WHERE s.id IN
<foreach collection="shops" item="shopId" open="(" separator="," close=")">
#{shopId}
</foreach>
AND po.shipping_invoice_number IS NOT NULL
</select>
</mapper>

View File

@ -779,4 +779,11 @@
FROM sku
WHERE image_source IS NOT NULL
</select>
<select id="fetchAllClientSkuCodes" resultType="java.lang.String">
SELECT erp_code
FROM sku
JOIN client_sku ON sku.id = client_sku.sku_id
JOIN client c ON client_sku.client_id = c.id
WHERE c.internal_code = #{clientCode};
</select>
</mapper>

View File

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

View File

@ -2,6 +2,7 @@ package org.jeecg.modules.business.service;
import org.jeecg.modules.business.entity.*;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.business.vo.Period;
import java.io.Serializable;
import java.nio.file.Path;
@ -48,4 +49,6 @@ public interface IShippingInvoiceService extends IService<ShippingInvoice> {
boolean deleteAttachmentFile(String filename);
void setPaid(List<String> invoiceNumbers);
Period getInvoicePeriod(List<String> shopIdList);
}

View File

@ -122,4 +122,6 @@ public interface ISkuService extends IService<Sku> {
List<SkuOrderPage> listSelectableSkuIds(String clientId);
List<Sku> listImgUrls();
List<String> fetchAllClientSkuCodes(String clientCode);
}

View File

@ -430,6 +430,12 @@ public class PlatformOrderShippingInvoiceService {
return factureDetailMapper.selectList(queryWrapper);
}
public List<FactureDetail> getInvoiceDetailByShopsAndPeriod(List<String> shopIds, String startDate, String endDate, String type) throws UserException {
if(!type.equals(InvoiceType.SHIPPING_INVOICE.getCode()) && !type.equals(InvoiceType.COMPLETE_INVOICE.getCode()))
throw new UserException("Invalid invoice type");
return factureDetailMapper.selectByShopsAndPeriod(shopIds, startDate, endDate, type);
}
public byte[] exportToExcel(List<FactureDetail> details, List<SavRefundWithDetail> refunds, List<ExtraFeeResult> extraFees, String invoiceNumber, String invoiceEntity, String internalCode) throws IOException {
SheetManager sheetManager = SheetManager.createXLSX();
sheetManager.startDetailsSheet();

View File

@ -26,10 +26,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
@ -79,7 +76,7 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
*/
@Transactional
@Override
public boolean addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData, AtomicReference<Map<String, LocalDateTime>> providersHistory) {
public List<String> addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData, AtomicReference<Map<String, LocalDateTime>> providersHistory) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String mabangUsername = sysUser.getMabangUsername();
String content = metaData.getFilename();
@ -118,7 +115,7 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
}
if(skuDataList.isEmpty()) {
log.error("Couldn't get SKU data from Mabang API for invoice : {}", metaData.getInvoiceCode());
return false;
return Collections.singletonList("Couldn't get SKU data from Mabang API for invoice : " + metaData.getInvoiceCode());
}
for(SkuData skuData : skuDataList) {
@ -130,7 +127,7 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
stockData.setStockSku(entry.getKey());
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());
stockData.setProvider(entry.getValue().getSupplier());
skuStockData.add(stockData);
}
// group by provider
@ -150,7 +147,7 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<String> errors = new ArrayList<>();
List<String> groupIds = new ArrayList<>(); // results from Mabang API
List<CompletableFuture<Boolean>> changeOrderFutures = stockProviderMap.entrySet().stream()
.map(entry -> CompletableFuture.supplyAsync(() -> {
@ -174,9 +171,10 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
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);
log.info("Mabang Add purchase API response | Invoice : {} - Provider : {} - Message : {}", metaData.getInvoiceCode(), providerName,response.toString());
if(response.getGroupId() == null) {
log.error("Failed to create purchase order to Mabang for Invoice : {} - Provider : {} - Skus : {} - Reason : {}", metaData.getInvoiceCode(), providerName, stockDataList.stream().map(SkuStockData::getStockSku).collect(Collectors.joining(",")), response.getMessage());
errors.add("Failed to create purchase order to Mabang for Invoice : " + metaData.getInvoiceCode() + " - Provider : " + providerName + " - Skus : " + stockDataList.stream().map(SkuStockData::getStockSku).collect(Collectors.joining(",")) + " - Reason : " + response.getMessage());
return false;
}
groupIds.add(response.getGroupId());
@ -192,8 +190,8 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
if(nbSuccesses == stockProviderMap.size()) {
purchaseOrderService.updatePurchaseOrderStatus(metaData.getInvoiceCode(), true);
purchaseOrderService.updatePurchaseOrderGroupIds(metaData.getInvoiceCode(), groupIds);
return true;
return Collections.emptyList();
}
return false;
return errors;
}
}

View File

@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.mapper.ShippingInvoiceMapper;
import org.jeecg.modules.business.service.IShippingInvoiceService;
import org.jeecg.modules.business.vo.Period;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@ -235,4 +236,9 @@ public class ShippingInvoiceServiceImpl extends ServiceImpl<ShippingInvoiceMappe
public void setPaid(List<String> invoiceNumbers) {
shippingInvoiceMapper.setPaid(invoiceNumbers);
}
@Override
public Period getInvoicePeriod(List<String> shopIdList) {
return shippingInvoiceMapper.getInvoicePeriod(shopIdList);
}
}

View File

@ -544,7 +544,7 @@ public class SkuListMabangServiceImpl extends ServiceImpl<SkuListMabangMapper, S
skuData.setDeclareNameEn(skuOrderPage.getDeclareEname());
skuData.setSalePrice(skuOrderPage.getSkuPrice());
skuData.setDeclareValue(skuOrderPage.getDeclaredValue());
skuData.setProvider(DEFAULT_WAREHOUSE_NAME);
skuData.setWarehouse(DEFAULT_WAREHOUSE_NAME);
if(skuOrderPage.getWeight() != null)
skuData.setSaleRemark(skuOrderPage.getWeight().toString());
skuData.setHasBattery(sensitiveAttribute.getHasBattery());

View File

@ -614,4 +614,9 @@ public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements ISkuS
return skuMapper.listImgUrls();
}
@Override
public List<String> fetchAllClientSkuCodes(String clientCode) {
return skuMapper.fetchAllClientSkuCodes(clientCode);
}
}

View File

@ -0,0 +1,32 @@
package org.jeecg.modules.business.vo;
public enum InvoiceType {
PURCHASE_INVOICE(1, "purchase_invoice"),
SHIPPING_INVOICE(2, "shipping_invoice"),
COMPLETE_INVOICE(7, "complete_invoice");
private final int code;
private final String text;
InvoiceType(int code, String text) {
this.code = code;
this.text = text;
}
public String getCode() {
return String.valueOf(code);
}
public String text() {
return text;
}
public static InvoiceType fromCode(Integer code) {
for (InvoiceType invoiceType : InvoiceType.values()) {
if (invoiceType.code == code) {
return invoiceType;
}
}
return null;
}
}

View File

@ -1,16 +1,18 @@
package org.jeecg.modules.business.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
@Data
public class Period {
@JsonProperty
@JsonProperty("start")
private final Date start;
@JsonProperty
@JsonProperty("end")
private final Date end;
@JsonProperty
@JsonProperty("type")
private final String type;
public Period(Date start, Date end, String type) {