Merge pull request #65 from LQYBill/feature/invoicesCRUD

Feature/invoices crud
pull/6221/head
Qiuyi LI 2024-03-28 11:25:51 +01:00 committed by GitHub
commit 72a5504995
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 1886 additions and 435 deletions

View File

@ -0,0 +1,23 @@
package org.jeecg.modules.business.controller;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "Security-安全管理")
@RestController
@RequestMapping("/security")
@Slf4j
public class SecurityController {
@GetMapping(value = "/isEmployee")
public Result<?> checkIsEmployee () {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
return Result.ok(!sysUser.getOrgCode().contains("A04"));
}
}

View File

@ -0,0 +1,46 @@
package org.jeecg.modules.business.controller.admin;
import cn.hutool.core.date.DateTime;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.business.service.DashboardService;
import org.jeecg.modules.system.service.impl.SysBaseApiImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
@Api(tags = "Admin-管理员管理")
@RestController
@RequestMapping("/admin")
@Slf4j
public class AdminController {
@Autowired private DashboardService dashboardService;
@Autowired private SysBaseApiImpl sysBaseApi;
@GetMapping(value = "/kpis")
public Result<?> kpis(@RequestParam(value = "period", required = false) String period) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
List<String> roles = sysBaseApi.getRolesByUsername(sysUser.getUsername());
LocalDateTime start = LocalDateTime.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))).minusMonths(11).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
LocalDateTime end = LocalDateTime.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT")));
if("currentYear".equals(period)){
start = LocalDateTime.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))).withMonth(1).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
} else if("currentMonth".equals(period)){
start = LocalDateTime.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
}
Map<String, ?> kpiMap = dashboardService.getKpis(start, end, roles, sysUser.getUsername());
return Result.ok(kpiMap);
}
}

View File

@ -0,0 +1,45 @@
package org.jeecg.modules.business.controller.admin;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.business.entity.Invoice;
import org.jeecg.modules.business.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Api(tags = "发票")
@RestController
@RequestMapping("/invoice")
@Slf4j
public class InvoiceViewController {
@Autowired
private InvoiceService invoiceService;
@GetMapping(value = "/list")
public Result<?> list(Invoice invoice, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<Invoice> queryWrapper = QueryGenerator.initQueryWrapper(invoice, req.getParameterMap());
Page<Invoice> page = new Page<>(pageNo, pageSize);
IPage<Invoice> pageList = invoiceService.page(page, queryWrapper);
return Result.ok(pageList);
}
@DeleteMapping(value = "/cancelInvoice")
public Result<?> cancelInvoice(@RequestParam("id") String id, @RequestParam("invoiceNumber") String invoiceNumber, @RequestParam("clientId") String clientId) {
log.info("Cancelling invoice number : {}", invoiceNumber);
boolean invoiceCancelled = invoiceService.cancelInvoice(id, invoiceNumber, clientId);
return Result.ok(invoiceCancelled ? "sys.api.invoiceCancelSuccess" : "sys.api.invoiceCancelSuccessFileDeleteFail");
}
@DeleteMapping(value="/cancelBatchInvoice")
public Result<?> cancelBatchInvoice(@RequestBody List<Invoice> invoices) {
boolean invoicesCancelled = invoiceService.cancelBatchInvoice(invoices);
return Result.ok(invoicesCancelled ? "sys.api.invoiceCancelSuccess" : "sys.api.invoiceCancelSuccessFileDeleteFail");
}
}

View File

@ -37,10 +37,12 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
@ -54,6 +56,10 @@ import java.util.stream.Collectors;
@RequestMapping("/purchaseOrder")
@Slf4j
public class PurchaseOrderController {
@Autowired
private IBalanceService balanceService;
@Autowired
private IClientCategoryService clientCategoryService;
@Autowired
private IPurchaseOrderService purchaseOrderService;
@Autowired
@ -85,9 +91,10 @@ public class PurchaseOrderController {
@ApiOperation(value = "商品采购订单-分页列表查询", notes = "商品采购订单-分页列表查询")
@GetMapping(value = "/list")
public Result<?> queryPageList(@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(name = "clientId", required = false) String clientId) {
Page<PurchaseOrderPage> page = new Page<>(pageNo, pageSize);
purchaseOrderService.setPageForList(page);
purchaseOrderService.setPageForList(page, clientId);
return Result.OK(page);
}
/**
@ -114,6 +121,7 @@ public class PurchaseOrderController {
@ApiOperation(value="商品采购订单-添加", notes="商品采购订单-添加")
@PostMapping(value = "/addPurchaseAndOrder")
public Result<?> addPurchaseAndOrder( @RequestBody PurchaseOrderPage purchaseOrderPage) {
Client client = clientService.getById(purchaseOrderPage.getClientId());
PurchaseOrder purchaseOrder = new PurchaseOrder();
BeanUtils.copyProperties(purchaseOrderPage, purchaseOrder);
purchaseOrder.setPaymentDocumentString(new String(purchaseOrderPage.getPaymentDocument()));
@ -121,13 +129,20 @@ public class PurchaseOrderController {
String purchaseID = UUID.randomUUID().toString();
purchaseOrder.setId(purchaseID);
purchaseOrderService.save(purchaseOrder);
String clientCategory = clientCategoryService.getClientCategoryByClientId(client.getId());
if(clientCategory.equals(ClientCategory.CategoryName.CONFIRMED.getName()) || clientCategory.equals(ClientCategory.CategoryName.VIP.getName())) {
balanceService.updateBalance(purchaseOrder.getClientId(), purchaseOrder.getInvoiceNumber(), "purchase");
}
// No need to attribute purchase order to platform orders, probably just buying stock
if(purchaseOrderPage.getPlatformOrderId() == null) {
return Result.OK("sys.api.entryAddSuccess");
}
List<String> platformOrderIds = Arrays.stream(purchaseOrderPage.getPlatformOrderId().split(","))
.map(String::trim)
.collect(Collectors.toList());
log.info("Creating new purchase order and attributing it to orders: {}", platformOrderIds);
log.info("Attributing purchase order to platform orders: {}", platformOrderIds);
List<PlatformOrder> platformOrders = platformOrderService.selectByPlatformOrderIds(platformOrderIds);
Map<String, List<String>> platformOrderIdUpdateMap = new HashMap<>();
if(!platformOrders.isEmpty()) {
@ -265,45 +280,6 @@ public class PurchaseOrderController {
return Result.OK("sys.api.entryBatchDeleteSuccess");
}
/**
* Cancel a purchase order's invoice.
* @param purchaseId purchase ID
* @param invoiceNumber invoice number
* @return
*/
@AutoLog(value = "商品采购订单-通过id删除")
@ApiOperation(value = "商品采购订单-通过id删除", notes = "商品采购订单-通过id删除")
@DeleteMapping(value = "/cancelInvoice")
public Result<?> cancelInvoice(@RequestParam("id") String purchaseId,
@RequestParam("invoiceNumber") String invoiceNumber) {
PurchaseOrder po = purchaseOrderService.getById(purchaseId);
if(po.getInventoryDocumentString() != null && !po.getInventoryDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getInventoryDocumentString());
if(po.getPaymentDocumentString() != null && !po.getPaymentDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getPaymentDocumentString());
purchaseOrderService.cancelInvoice(purchaseId, invoiceNumber);
return Result.OK("sys.api.entryDeleteSuccess");
}
/**
* Cancel multiple purchase order's invoice.
* @param purchaseIds list of purchase IDs
* @return
*/
@AutoLog(value = "商品采购订单-批量删除")
@ApiOperation(value = "商品采购订单-批量删除", notes = "商品采购订单-批量删除")
@DeleteMapping(value = "/cancelBatchInvoice")
public Result<?> cancelBatchInvoice(@RequestParam("ids") String purchaseIds) {
List<PurchaseOrder> purchaseOrders = purchaseOrderService.listByIds(Arrays.asList(purchaseIds.split(",")));
for(PurchaseOrder po : purchaseOrders) {
if(po.getInventoryDocumentString() != null && !po.getInventoryDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getInventoryDocumentString());
if(po.getPaymentDocumentString() != null && !po.getPaymentDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getPaymentDocumentString());
}
purchaseOrderService.cancelBatchInvoice(purchaseIds);
return Result.OK("sys.api.entryDeleteSuccess");
}
/**
* id
*
@ -502,7 +478,7 @@ public class PurchaseOrderController {
* @param purchaseID purchaseID
*/
@RequestMapping(value = "/invoiceMeta", method = RequestMethod.GET)
public InvoiceMetaData getInvoiceMetaData(@RequestParam String purchaseID, HttpServletResponse response) throws IOException, URISyntaxException {
public InvoiceMetaData getInvoiceMetaData(@RequestParam String purchaseID, HttpServletResponse response) throws IOException, URISyntaxException, UserException {
return purchaseOrderService.makeInvoice(purchaseID);
}
@ -594,29 +570,37 @@ public class PurchaseOrderController {
}
@GetMapping(value = "/createMabangPurchaseOrder")
public Result<?> createMabangPurchaseOrder(@RequestParam("invoiceNumbers") List<String> request) {
log.info("Creating purchase order to Mabang for invoices : {} ", request);
public Result<?> createMabangPurchaseOrder(@RequestParam("invoiceNumbers") List<String> invoiceNumbers) {
log.info("Creating purchase order to Mabang for invoices : {} ", invoiceNumbers);
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<CompletableFuture<Boolean>> changeOrderFutures = request.stream()
// providersHistory lists the providers that have already been processed, if the current provider is in the list and has been processed within the last 10 seconds, the thread will sleep for 10 seconds
AtomicReference<Map<String, LocalDateTime>> providersHistory = new AtomicReference<>(new HashMap<>());
List<CompletableFuture<String>> future = invoiceNumbers.stream()
.map(invoiceNumber -> CompletableFuture.supplyAsync(() -> {
log.info("Invoice number : {}", invoiceNumber);
List<SkuQuantity> skuQuantities = purchaseOrderService.getSkuQuantityByInvoiceNumber(invoiceNumber);
if(skuQuantities.isEmpty()) {
return false;
return null;
}
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);
boolean success = providerMabangService.addPurchaseOrderToMabang(skuQuantityMap, metaData, providersHistory);
return success ? invoiceNumber : "failed";
},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());
List<String> results = future.stream().map(CompletableFuture::join).collect(Collectors.toList());
long nbSuccesses = results.stream().filter(Objects::nonNull).count();
log.info("{}/{} purchase order requests have succeeded.", nbSuccesses, invoiceNumbers.size());
return Result.ok("data.noData");
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());
data.put("fail", failedInvoices);
data.put("success", successInvoices);
return Result.OK(data);
}
}

View File

@ -63,6 +63,7 @@ public class SavRefundController extends JeecgController<SavRefund, ISavRefundSe
public Result<?> queryPageList(SavRefundWithDetail savRefund,
@RequestParam(name = "shop", defaultValue = "") String shop,
@RequestParam(name = "orderID", defaultValue = "") String platformOrderId,
@RequestParam(name = "invoiceNumber", defaultValue = "") String invoiceNumber,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(name = "column", defaultValue = "invoice_umber") String column,
@ -78,10 +79,11 @@ public class SavRefundController extends JeecgController<SavRefund, ISavRefundSe
}
String parsedShop = "%"+shop.toUpperCase()+"%";
String parsedPlatformOrderId = "%"+platformOrderId+"%";
String parsedInvoiceNumber = "%"+invoiceNumber+"%";
if(!order.equalsIgnoreCase("ASC") && !order.equalsIgnoreCase("DESC")) {
return Result.error("Error 400 Bad Request");
}
List<SavRefundWithDetail> refundList = savRefundWithDetailService.fetchRefundsWhere(parsedShop, parsedPlatformOrderId, parsedColumn, order);
List<SavRefundWithDetail> refundList = savRefundWithDetailService.fetchRefundsWhere(parsedShop, parsedPlatformOrderId, parsedInvoiceNumber, parsedColumn, order);
IPage<SavRefundWithDetail> pageList = new Page<>(pageNo, pageSize);
pageList.setRecords(refundList);
return Result.OK(pageList);

View File

@ -55,6 +55,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.jeecg.modules.business.entity.Invoice.InvoiceType.*;
import static org.jeecg.modules.business.entity.Task.TaskCode.SI_G;
import static org.jeecg.modules.business.entity.TaskHistory.TaskStatus.*;
@ -431,6 +432,11 @@ public class InvoiceController {
}
try {
String purchaseId = purchaseOrderService.addPurchase(skuQuantities);
PurchaseOrder purchaseOrder = purchaseOrderService.getById(purchaseId);
String clientCategory = clientCategoryService.getClientCategoryByClientId(purchaseOrder.getClientId());
if(clientCategory.equals(ClientCategory.CategoryName.CONFIRMED.getName()) || clientCategory.equals(ClientCategory.CategoryName.VIP.getName())) {
balanceService.updateBalance(purchaseOrder.getClientId(), purchaseOrder.getInvoiceNumber(), "purchase");
}
metaData = purchaseOrderService.makeInvoice(purchaseId);
return Result.OK(metaData);
} catch (UserException e) {
@ -691,6 +697,13 @@ public class InvoiceController {
List<SavRefundWithDetail> refunds = savRefundWithDetailService.getRefundsByInvoiceNumber(invoiceNumber);
return shippingInvoiceService.exportToExcel(factureDetails, refunds, invoiceNumber, invoiceEntity, internalCode);
}
@GetMapping(value = "/downloadInventory")
public byte[] downloadInventory(@RequestParam("invoiceCode") String invoiceCode, @RequestParam("internalCode") String internalCode, @RequestParam("invoiceEntity") String invoiceEntity) throws IOException {
InvoiceMetaData metaData = new InvoiceMetaData("", invoiceCode, internalCode, invoiceEntity, "");
List<SkuOrderPage> skuOrderPages = skuService.getInventoryByInvoiceNumber(metaData.getInvoiceCode());
System.out.println(skuOrderPages);
return shippingInvoiceService.exportPurchaseInventoryToExcel(skuOrderPages, metaData);
}
/**
* Returns a breakdown of all invoicable shops
@ -949,7 +962,7 @@ public class InvoiceController {
String email = sysUser.getEmail();
String invoiceID;
String customerFullName;
boolean isShippingInvoice = invoiceNumber.charAt(8) == '7' || invoiceNumber.charAt(8) == '2';
boolean isShippingInvoice = Invoice.getType(invoiceNumber).equalsIgnoreCase(COMPLETE.name()) || Invoice.getType(invoiceNumber).equalsIgnoreCase(SHIPPING.name());
if(isShippingInvoice)
invoiceID = iShippingInvoiceService.getShippingInvoiceId(invoiceNumber);
else

View File

@ -13,6 +13,7 @@ import org.jeecg.common.aspect.annotation.AutoLog;
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.entity.Client;
import org.jeecg.modules.business.entity.ShippingInvoice;
import org.jeecg.modules.business.service.*;
@ -282,8 +283,8 @@ public class ShippingInvoiceController {
* @throws IOException
*/
@GetMapping(value = "/downloadCompleteInvoiceExcel")
public ResponseEntity<?> download(@RequestParam("invoiceNumber") String invoiceNumber, @RequestParam("filetype") String filetype) throws IOException {
String filename = shippingInvoiceService.getInvoiceList(invoiceNumber, filetype);
public ResponseEntity<?> download(@RequestParam("invoiceNumber") String invoiceNumber, @RequestParam("filetype") String filetype) throws IOException, UserException {
String filename = platformOrderShippingInvoiceService.getInvoiceList(invoiceNumber, filetype);
if(!filename.equals("ERROR")) {
File file = new File(filename);
@ -349,7 +350,7 @@ public class ShippingInvoiceController {
public Result<?> sendDetailsByEmail(@RequestParam("invoiceNumber") String invoiceNumber,
@RequestParam("email") String email,
@RequestParam("invoiceEntity") String invoiceEntity) throws Exception {
String filePath = shippingInvoiceService.getInvoiceList(invoiceNumber, "detail");
String filePath = platformOrderShippingInvoiceService.getInvoiceList(invoiceNumber, "detail");
String fileType = "Détails de facture";
String subject = "Détails de facture N°" + invoiceNumber;
Properties prop = emailService.getMailSender();
@ -394,129 +395,20 @@ public class ShippingInvoiceController {
return Result.OK(client);
}
/**
* Deletes an invoice
* @param invoiceNumber invoice number to cancel
* @return if update successful
*/
@PostMapping(value = "/cancelInvoice")
public Result<?> cancelInvoice(@RequestParam("id") String id, @RequestParam("invoiceNumber") String invoiceNumber, @RequestParam("clientId") String clientId) {
log.info("Cancelling invoice number : {}", invoiceNumber);
platformOrderContentService.cancelInvoice(invoiceNumber);
platformOrderService.cancelInvoice(invoiceNumber);
purchaseOrderService.cancelInvoice(invoiceNumber);
savRefundService.cancelInvoice(invoiceNumber);
shippingInvoiceService.delMain(id);
log.info("Updating balance ...");
balanceService.deleteBalance(id, OperationType.Debit.name());
log.info("Deleting invoice files ...");
String invoiceEntity = clientService.getClientEntity(clientId);
List<Path> invoicePathList = shippingInvoiceService.getPath(INVOICE_LOCATION, invoiceNumber, invoiceEntity);
List<Path> detailPathList = shippingInvoiceService.getPath(INVOICE_DETAIL_LOCATION, invoiceNumber, invoiceEntity);
boolean invoiceDeleted = false, detailDeleted = false;
@PostMapping(value = "/setPaid")
public Result<?> setPaid(@RequestParam("shipping") List<String> shippingNumbers, @RequestParam("purchase") List<String> purchaseNumbers) {
log.info("Setting invoice numbers : \n" +
" - shipping : {} to paid.\n" +
" - purchase : {} to paid.", shippingNumbers, purchaseNumbers);
if(invoicePathList.isEmpty()) {
log.error("FILE NOT FOUND : " + invoiceNumber);
} else {
for (Path path : invoicePathList) {
log.info(path.toString());
}
try {
File invoiceFile = new File(invoicePathList.get(0).toString());
if(invoiceFile.delete()) {
log.info("Invoice file {} delete successful.", invoicePathList.get(0).toString());
invoiceDeleted = true;
} else {
log.error("Invoice file delete fail.");
}
} catch (Exception e) {
e.printStackTrace();
}
if(purchaseNumbers.isEmpty() && shippingNumbers.isEmpty()) {
return Result.error("No invoice numbers found.");
}
if(detailPathList.isEmpty()) {
log.error("DETAIL FILE NOT FOUND : " + invoiceNumber);
} else {
for (Path path : detailPathList) {
log.info(path.toString());
}
try {
File detailFile = new File(detailPathList.get(0).toString());
if(detailFile.delete()) {
log.info("Detail file {} delete successful.", detailPathList.get(0).toString());
detailDeleted = true;
} else {
log.error("Detail file delete fail.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
log.info("Invoice files deleted.");
return Result.ok("Invoice cancel successful." + (invoiceDeleted ? "" : " Failed to delete invoice file.") + (detailDeleted ? "" : " Failed to delete detail file."));
}
/**
* Delete a batch of invoices
* @param ids list of invoice ids
* @param invoiceNumbers list of invoice numbers
* @param clientIds list of clients
* @return result
*/
@PostMapping(value = "/cancelBatchInvoice")
public Result<?> cancelBatchInvoice(@RequestParam("ids") List<String> ids, @RequestParam("invoiceNumbers") List<String> invoiceNumbers, @RequestParam("clientIds") List<String> clientIds) {
log.info("Cancelling invoices : {}", invoiceNumbers);
purchaseOrderService.cancelBatchInvoice(invoiceNumbers);
platformOrderContentService.cancelBatchInvoice(invoiceNumbers);
platformOrderService.cancelBatchInvoice(invoiceNumbers);
savRefundService.cancelBatchInvoice(invoiceNumbers);
shippingInvoiceService.delBatchMain(ids);
log.info("Updating balances ...");
balanceService.deleteBatchBalance(ids, OperationType.Debit.name());
log.info("Deleting invoice files ...");
for(int i = 0; i < ids.size(); i++) {
String invoiceNumber = invoiceNumbers.get(i);
String invoiceEntity = clientService.getClientEntity(clientIds.get(i));
List<Path> invoicePathList = shippingInvoiceService.getPath(INVOICE_LOCATION, invoiceNumber, invoiceEntity);
List<Path> detailPathList = shippingInvoiceService.getPath(INVOICE_DETAIL_LOCATION, invoiceNumber, invoiceEntity);
if(invoicePathList.isEmpty()) {
log.error("FILE NOT FOUND : " + invoiceNumber + ", " + invoiceEntity);
} else {
for (Path path : invoicePathList) {
log.info(path.toString());
}
try {
File invoiceFile = new File(invoicePathList.get(0).toString());
if(invoiceFile.delete()) {
log.info("Invoice file {} delete successful.", invoicePathList.get(0).toString());
} else {
log.error("Invoice file delete fail.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
if(detailPathList.isEmpty()) {
log.error("DETAIL FILE NOT FOUND : " + invoiceNumber + ", " + invoiceEntity);
} else {
for (Path path : detailPathList) {
log.info(path.toString());
}
try {
File detailFile = new File(detailPathList.get(0).toString());
if(detailFile.delete()) {
log.info("Detail file {} delete successful.", detailPathList.get(0).toString());
} else {
log.error("Detail file {} delete fail.", detailPathList.get(0).toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
log.info("End of invoice files deletion.");
return Result.ok("Invoices cancellation finished.");
if(!purchaseNumbers.isEmpty())
purchaseOrderService.setPaid(purchaseNumbers);
if(!shippingNumbers.isEmpty())
shippingInvoiceService.setPaid(shippingNumbers);
return Result.ok("Invoice set to paid.");
}
}

View File

@ -47,6 +47,7 @@ public abstract class Request {
headers.add(HttpHeaders.CONTENT_TYPE, "application/json");
String bodyString = generateJsonBodyString(body);
log.info("JSON SENT : " + bodyString);
String signature = authorization(bodyString);
headers.add("Authorization", signature);
while (attempts++ < 5){

View File

@ -50,7 +50,7 @@ public class SkuListRawStream implements NetworkDataStream<SkuListResponse> {
throw new IllegalStateException("Calling hasNext before begin");
}
// no page left, false
if(currentResponse.getCursor().isEmpty()) {
if(currentResponse.getCursor().isEmpty() || toSend.getCursor().isEmpty()) {
log.info("No page left, end");
return false;
}

View File

@ -0,0 +1,26 @@
package org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import java.util.List;
@Data
public class SkuStockData {
@JSONField(name = "stockSku")
private String stockSku;
@JSONField(name = "stockQuantity")
private Integer stockQuantity;
@JSONField(name = "warehouse")
private List<WarehouseStock> warehouse;
public WarehouseStock getWarehouseStock(String warehouseName) {
for (WarehouseStock stock : warehouse) {
if (stock.getWarehouseName().equals(warehouseName)) {
return stock;
}
}
return null;
}
}

View File

@ -0,0 +1,73 @@
package org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.domain.api.mabang.getorderlist.NetworkDataStream;
import java.util.NoSuchElementException;
/**
* This stream control reception of the response of the mabang order list API
*/
@Slf4j
public class SkuStockRawStream implements NetworkDataStream<SkuStockResponse> {
/**
* Instance's current request.
*/
private final SkuStockRequestBody toSend;
/**
* Response of last request.
*/
private SkuStockResponse currentResponse;
private boolean began;
public SkuStockRawStream(SkuStockRequestBody firstBody) {
this.toSend = firstBody;
this.currentResponse = null;
began = false;
}
@Override
public SkuStockResponse attempt() {
log.info("Begin the first request");
this.currentResponse = new SkuStockRequest(toSend).send();
if (currentResponse.getData().isEmpty()) {
return null;
}
began = true;
toSend.nextPage();
return currentResponse;
}
/**
* Check whether there are still order left, network communication is done here.
*
* @return true if there are, otherwise false.
*/
@Override
public boolean hasNext() {
if (!began) {
throw new IllegalStateException("Calling hasNext before begin");
}
// no page left, false
log.info("No page left, end");
return false;
}
/**
* Get next Order.
*
* @return next order.
* @throws NoSuchElementException if data is already empty.
*/
@Override
public SkuStockResponse next() {
if (!hasNext())
throw new NoSuchElementException();
log.info("Sending request for page {}/{}.", toSend.getPage(), toSend.getTotal() == null ? "?" : toSend.getTotalPages());
this.currentResponse = new SkuStockRequest(toSend).send();
toSend.nextPage();
return this.currentResponse;
}
}

View File

@ -0,0 +1,34 @@
package org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.domain.api.mabang.Request;
import org.jeecg.modules.business.domain.api.mabang.doSearchSkuListNew.SkuListRequestBody;
import org.jeecg.modules.business.domain.api.mabang.doSearchSkuListNew.SkuListResponse;
import org.springframework.http.ResponseEntity;
/**
* This class contains some key information and necessary procedures
* to send a request to mabang "get order list" API, for example: target URL,
* correspondent HTTP method, procedure to generate authorization.
* <p>
* One can use static method {@code sendRequest} to send request with body,
* and then get respective response. Or use instance of this class, see below.
* <p>
* Because data returned by target API is paginated. One can retrieve all data
* by calling next and hasNext.
*/
@Slf4j
public class SkuStockRequest extends Request {
public SkuStockRequest(SkuStockRequestBody body) {
super(body);
}
@Override
public SkuStockResponse send() {
ResponseEntity<String> res = rawSend();
return SkuStockResponse.parse(JSON.parseObject(res.getBody()));
}
}

View File

@ -0,0 +1,75 @@
package org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity;
import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import lombok.Setter;
import org.jeecg.modules.business.domain.api.mabang.RequestBody;
import org.jeecg.modules.business.domain.api.mabang.doSearchSkuListNew.DateType;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.function.Function;
@Getter
@Setter
public class SkuStockRequestBody implements RequestBody {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// max 100, seperated by comma
private String stockSkus = "";
// day format
private LocalDateTime updateTime;
private static final String DEFAULT_WAREHOUSE_NAME = "SZBA宝安仓";
private Integer page = 1;
private Integer total;
@Override
public String api() {
return "stock-get-stock-quantity";
}
@Override
public JSONObject parameters() {
JSONObject json = new JSONObject();
putNonNull(json, "stockSkus", stockSkus);
putNonNull(json, "updateTime", updateTime, formatter::format);
putNonNull(json, "warehouseName", DEFAULT_WAREHOUSE_NAME);
return json;
}
void nextPage() {
setPage(this.page + 1);
}
int getPage() {
return page;
}
public SkuStockRequestBody setStockSkus(String stockSkus) {
this.stockSkus = stockSkus;
return this;
}
public SkuStockRequestBody setPage(int page) {
this.page = page;
return this;
}
public SkuStockRequestBody setTotal(int total) {
this.total = total;
return this;
}
public int getTotalPages() {
return (int) Math.ceil((double) total / 100);
}
private <E> void putNonNull(JSONObject json, String key, E value) {
if (value != null) {
json.put(key, value);
}
}
private <E, T> void putNonNull(JSONObject json, String key, E value, Function<E, T> mapper) {
if (value != null) {
json.put(key, mapper.apply(value));
}
}
}

View File

@ -0,0 +1,65 @@
package org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.domain.api.mabang.Response;
import org.jeecg.modules.business.domain.api.mabang.doSearchSkuListNew.SkuListRequestErrorException;
/**
* Immutable object
*/
@Slf4j
@Getter
public class SkuStockResponse extends Response {
/**
* Current page data
*/
private final JSONArray data;
private final JSONObject rawData;
SkuStockResponse(Code code, JSONArray data, JSONObject rawData) {
super(code);
this.data = data;
this.rawData = rawData;
}
/**
* Make an instance by parsing json, it only checks validity of code.
* if json is not valid, return null
*
* @param json the json to parse
* @return Instance
* @throws SkuListRequestErrorException if response code represents error.
*/
public static SkuStockResponse parse(JSONObject json) throws SkuListRequestErrorException {
log.debug("Constructing a response by json.");
String code = json.getString("code");
if (code.equals(Code.ERROR.value))
throw new SkuListRequestErrorException(json.getString("message"));
JSONObject data = json.getJSONObject("data");
System.out.println(data);
JSONArray realData = data.getJSONArray("data");
if(realData != null) {
log.info("Constructed response: data contained {}", realData.size());
}
else {
log.info("Data is null");
}
return new SkuStockResponse(Code.SUCCESS, realData, json);
}
public JSONObject getRawDate() {
return rawData;
}
@Override
public String toString() {
return "SkuStockResponse{" +
", data=" + data +
'}';
}
}

View File

@ -0,0 +1,118 @@
package org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.domain.api.mabang.getorderlist.NetworkDataStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
/**
* This class provide stream of order.
*/
@Slf4j
public class SkuStockStream implements NetworkDataStream<SkuStockData> {
private final NetworkDataStream<SkuStockResponse> rawStream;
private List<SkuStockData> skus;
private int index;
private boolean began;
/**
* Flag of current data is already empty,
* either currentOrders is null or currentIndex arrives at the end.
* In both case, we should call next() of the rawStream.
*/
private boolean empty;
public SkuStockStream(NetworkDataStream<SkuStockResponse> rawStream) {
this.rawStream = rawStream;
skus = null;
this.index = 0;
this.empty = true;
this.began = false;
}
@Override
public List<SkuStockData> all() {
SkuStockData firstElement = attempt();
if (firstElement == null) {
return Collections.emptyList();
}
ArrayList<SkuStockData> res = new ArrayList<>();
res.add(firstElement);
while (hasNext()) {
SkuStockData nextSku = next();
res.add(nextSku);
}
return res;
}
@Override
public SkuStockData attempt() {
began = true;
log.info("Attempting for the first request");
SkuStockResponse response = rawStream.attempt();
if (response == null) {
log.info("No response");
return null;
}
if (response.getData().isEmpty()) {
log.info("Response with empty data");
return null;
}
skus = response.getData().toJavaList(SkuStockData.class);
index = 1;
log.info("Returned the first element");
empty = index >= skus.size();
return skus.get(0);
}
@Override
public boolean hasNext() {
// the first time
if (!began) {
throw new IllegalStateException("Calling hasNext before begin");
}
// Current data is not yet empty
if (index < skus.size()) {
log.debug("Current order list is not empty yet");
return true;
}
/* Current data is empty */
this.empty = true;
log.debug("Current order list is already empty,");
// and raw stream is empty too.
if (!rawStream.hasNext()) {
log.debug("and source stream is empty too, hasNext: false");
return false;
}
// but raw stream not empty.
else {
log.debug("but source stream still has data, hasNext: true");
return true;
}
}
@Override
public SkuStockData next() {
if (!hasNext()) {
throw new NoSuchElementException("Stream is empty!");
}
if (empty) {
skus = this.rawStream.next().getData().toJavaList(SkuStockData.class);
empty = false;
index = 0;
}
log.debug("Return data at {}", index);
SkuStockData res = skus.get(index);
index++;
return res;
}
}

View File

@ -0,0 +1,35 @@
package org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
@Data
public class WarehouseStock {
/**仓库编号**/
@JSONField(name = "warehouseId")
private String warehouseId;
/**仓库名称**/
@JSONField(name = "warehouseName")
private String warehouseName;
/**库存总数**/
@JSONField(name = "stockQuantity")
private Integer stockQuantity;
/**未发货数量 qty in platform orders**/
@JSONField(name = "waitingQuantity")
private Integer waitingQuantity;
/**调拨在途**/
@JSONField(name = "allotShippingQuantity")
private Integer allotShippingQuantity;
/**采购在途数量 qty in mabang purchase orders**/
@JSONField(name = "shippingQuantity")
private Integer shippingQuantity;
/**加工在途量**/
@JSONField(name = "processingQuantity")
private Integer processingQuantity;
/**fba未发货量**/
@JSONField(name = "fbaWaitingQuantity")
private Integer fbaWaitingQuantity;
/**仓位**/
@JSONField(name = "gridCode")
private String gridCode;
}

View File

@ -0,0 +1,73 @@
package org.jeecg.modules.business.domain.job;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity.SkuStockData;
import org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity.SkuStockRawStream;
import org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity.SkuStockRequestBody;
import org.jeecg.modules.business.domain.api.mabang.stockGetStockQuantity.SkuStockStream;
import org.jeecg.modules.business.entity.Sku;
import org.jeecg.modules.business.service.ISkuService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Component
public class MabangSkuStockUpdateJob implements Job {
@Autowired
private ISkuService skuService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
List<String> skuList = skuService.listSkus().stream().map(Sku::getErpCode).collect(Collectors.toList());
StringBuilder skus = new StringBuilder();
List<SkuStockData> updateList = new ArrayList<>();
List<Sku> skuToUpdate = new ArrayList<>();
log.info("Sku stock update Job has started.");
int count = 1;
for(int i = 1; i <= skuList.size(); i++) {
if(i%100 != 1)
skus.append(",");
skus.append(skuList.get(i - 1));
if(i%100 == 0) {
SkuStockRequestBody body = (new SkuStockRequestBody())
.setStockSkus(skus.toString())
.setTotal(skuList.size());
log.info("Sending request for page {}/{}.", count++, body.getTotalPages());
SkuStockRawStream rawStream = new SkuStockRawStream(body);
SkuStockStream stream = new SkuStockStream(rawStream);
updateList.addAll(stream.all());
skus = new StringBuilder();
}
}
if(skus.length() != 0) {
SkuStockRequestBody body = (new SkuStockRequestBody())
.setStockSkus(skus.toString())
.setTotal(skuList.size());
SkuStockRawStream rawStream = new SkuStockRawStream(body);
SkuStockStream stream = new SkuStockStream(rawStream);
updateList.addAll(stream.all());
}
updateList.forEach(skuStockData -> {
Sku sku = skuService.getByErpCode(skuStockData.getStockSku());
Integer availableAmount = skuStockData.getWarehouseStock("SZBA宝安仓").getStockQuantity();
Integer purchasingAmount = skuStockData.getWarehouseStock("SZBA宝安仓").getShippingQuantity();
if(sku.getAvailableAmount().equals(availableAmount) && sku.getPurchasingAmount().equals(purchasingAmount)) {
return;
}
sku.setAvailableAmount(availableAmount);
sku.setPurchasingAmount(purchasingAmount);
skuToUpdate.add(sku);
});
if(skuToUpdate.isEmpty()) {
return;
}
log.info("Updating stock for {} skus.", skuToUpdate.size());
skuService.updateBatchStockByIds(skuToUpdate);
}
}

View File

@ -9,6 +9,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.jeecg.modules.business.controller.UserException;
import org.jeecg.modules.business.domain.codeGeneration.CompleteInvoiceCodeRule;
import org.jeecg.modules.business.domain.codeGeneration.ShippingInvoiceCodeRule;
import org.jeecg.modules.business.domain.purchase.invoice.PurchaseInvoice;
import org.jeecg.modules.business.domain.purchase.invoice.PurchaseInvoiceEntry;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.mapper.*;
@ -19,6 +20,7 @@ import org.jeecg.modules.business.vo.SkuQuantity;
import org.jeecg.modules.business.vo.SkuWeightDiscountServiceFees;
import org.jeecg.modules.business.vo.clientPlatformOrder.section.OrdersStatisticData;
import org.jetbrains.annotations.NotNull;
import org.simpleframework.xml.core.Complete;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
@ -487,6 +489,7 @@ public class ShippingInvoiceFactory {
SUBJECT_FORMAT.format(end)
);
uninvoicedOrderToContent = platformOrderService.findUninvoicedOrders(shopIds, begin, end, warehouses);
}
else if (erpStatuses.toString().equals("[1, 2]") || erpStatuses.toString().equals("[1]")) {
subject = String.format(
@ -495,6 +498,7 @@ public class ShippingInvoiceFactory {
SUBJECT_FORMAT.format(end)
);
uninvoicedOrderToContent = platformOrderService.findUninvoicedOrderContentsForShopsAndStatus(shopIds, begin, end, erpStatuses, warehouses);
}
else {
subject = String.format(
@ -565,8 +569,11 @@ public class ShippingInvoiceFactory {
shops.forEach(shop -> shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee()));
String invoiceCode = generateInvoiceCode();
log.info("New invoice code: {}", invoiceCode);
calculateFees(null, logisticChannelMap, orderAndContent, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
Map<String, List<String>> errorMsg = calculateFees(null, logisticChannelMap, orderAndContent, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
latestDeclaredValues, client, shopServiceFeeMap, shopPackageMatFeeMap, invoiceCode);
if(!errorMsg.isEmpty()) {
errorMsg.forEach((k, v) -> log.error("Couldn't invoice orders for reason : {} : {}", k, v));
}
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
if (savRefunds != null) {
updateSavRefundsInDb(savRefunds, invoiceCode);
@ -1292,4 +1299,51 @@ public class ShippingInvoiceFactory {
}
return estimations;
}
@Transactional
public ShippingInvoice buildExistingShippingInvoice(String invoiceCode, String clientId, String start, String end, String filetype, String shippingMethod ) throws UserException {
log.info("Building existing shipping invoice : {} - Client ID : {}, ", invoiceCode, clientId);
Map<PlatformOrder, List<PlatformOrderContent>> ordersMapContent = platformOrderService.fetchOrderDataByInvoiceCode(invoiceCode);
List<SavRefundWithDetail> savRefunds = savRefundWithDetailService.getRefundsByInvoiceNumber(invoiceCode);
String subject;
if(shippingMethod.equals("post-shipping")) {
subject = String.format("Shipping fees from %s to %s", start, end);
}
else if(shippingMethod.equals("pre-shipping")) {
subject = String.format("Pre-Shipping fees, order time from %s to %s", start, end);
}
else
throw new UserException("Couldn't create shipping invoice of unknown type.");
Client client = clientMapper.selectById(clientId);
BigDecimal eurToUsd = exchangeRatesMapper.getExchangeRateFromDate("EUR", "USD", start);
return new ShippingInvoice(client, invoiceCode, subject, ordersMapContent, savRefunds, eurToUsd);
}
public PurchaseInvoice buildExistingPurchaseInvoice (String invoiceCode) {
PurchaseOrder order = purchaseOrderService.getPurchaseByInvoiceNumber(invoiceCode);
String purchaseId = order.getId();
Client client = clientMapper.getClientFromPurchase(purchaseId);
List<PurchaseInvoiceEntry> purchaseOrderSkuList = purchaseOrderContentMapper.selectInvoiceDataByID(purchaseId);
List<PromotionDetail> promotionDetails = skuPromotionHistoryMapper.selectPromotionByPurchase(purchaseId);
BigDecimal eurToUsd = exchangeRatesMapper.getExchangeRateFromDate("EUR", "USD", order.getCreateTime().toString());
return new PurchaseInvoice(client, invoiceCode, "Purchase Invoice", purchaseOrderSkuList, promotionDetails, eurToUsd);
}
public CompleteInvoice buildExistingCompleteInvoice(String invoiceCode, String clientId, String start, String end, String filetype, String shippingMethod) throws UserException {
log.info("Building existing complete invoice : {} - Client ID : {}, ", invoiceCode, clientId);
Map<PlatformOrder, List<PlatformOrderContent>> ordersMapContent = platformOrderService.fetchOrderDataByInvoiceCode(invoiceCode);
List<SavRefundWithDetail> savRefunds = savRefundWithDetailService.getRefundsByInvoiceNumber(invoiceCode);
String subject;
if(shippingMethod.equals("post-shipping"))
subject = String.format("Purchase and Shipping fees from %s to %s", start, end);
else if(shippingMethod.equals("pre-shipping"))
subject = String.format("Purchase and Pre-Shipping fees, order time from %s to %s", start, end);
else throw new UserException("Couldn't create complete invoice for unknown shipping method");
Client client = clientMapper.selectById(clientId);
BigDecimal eurToUsd = exchangeRatesMapper.getExchangeRateFromDate("EUR", "USD", start);
String purchaseID = purchaseOrderService.getInvoiceId(invoiceCode);
List<PurchaseInvoiceEntry> purchaseOrderSkuList = purchaseOrderContentMapper.selectInvoiceDataByID(purchaseID);
List<PromotionDetail> promotionDetails = skuPromotionHistoryMapper.selectPromotionByPurchase(purchaseID);
return new CompleteInvoice(client, invoiceCode, subject, ordersMapContent, savRefunds,
purchaseOrderSkuList, promotionDetails, eurToUsd);
}
}

View File

@ -0,0 +1,118 @@
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 lombok.Getter;
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.math.BigDecimal;
import java.util.Date;
/**
* @Description:
* @Author: jeecg-boot
* @Date: 2022-12-20
* @Version: V1.0
*/
@ApiModel(value = "invoice对象", description = "发票")
@Data
@TableName("all_invoices")
public class Invoice implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
@TableId(type = IdType.ASSIGN_UUID)
@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;
/**
* ID
*/
@Dict(dictTable = "client", dicText = "internal_code", dicCode = "id")
@Excel(name = "客户", width = 15)
@ApiModelProperty(value = "客户")
private String clientId;
/**
* currency ID
* */
@Dict(dictTable = "currency", dicText = "code", dicCode = "id")
@Excel(name = "currencyID", width = 15)
@ApiModelProperty(value = "currency ID")
private String currencyId;
/**
*
*/
@Excel(name = "发票号码", width = 15)
@ApiModelProperty(value = "发票号码")
private String invoiceNumber;
/**
*
*/
@Excel(name = "应付金额", width = 15)
@ApiModelProperty(value = "应付金额")
private BigDecimal totalAmount;
/**
*
*/
@Excel(name = "减免金额", width = 15)
@ApiModelProperty(value = "减免金额")
private BigDecimal discountAmount;
/**
*
*/
@Excel(name = "最终金额", width = 15)
@ApiModelProperty(value = "最终金额")
private BigDecimal finalAmount;
/**
*
*/
@Excel(name = "已付金额", width = 15)
@ApiModelProperty(value = "已付金额")
private BigDecimal paidAmount;
@Excel(name = "type", width = 15)
@ApiModelProperty(value = "type")
private String type;
@Getter
public enum InvoiceType {
PURCHASE('1'),
SHIPPING('2'),
COMPLETE('7');
private final char type;
InvoiceType(char type) {
this.type = type;
}
}
public static String getType(String invoiceNumber) {
for(InvoiceType type : InvoiceType.values()) {
if(type.getType() == invoiceNumber.charAt(8))
return type.name();
}
throw new IllegalArgumentException("Incorrect invoice number : " + invoiceNumber);
}
}

View File

@ -30,7 +30,7 @@ public class ShippingInvoice implements Serializable {
/**
*
*/
@TableId(type = IdType.ASSIGN_ID)
@TableId(type = IdType.ASSIGN_UUID)
@ApiModelProperty(value = "主键")
private String id;
/**

View File

@ -17,4 +17,6 @@ import java.math.BigDecimal;
public interface ExchangeRatesMapper extends BaseMapper<ExchangeRates> {
BigDecimal getLatestExchangeRate(@Param("original") String from, @Param("target") String to);
BigDecimal getExchangeRateFromDate(@Param("original") String from, @Param("target") String to, @Param("datetime") String dateTime);
}

View File

@ -0,0 +1,9 @@
package org.jeecg.modules.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.business.entity.Invoice;
import org.springframework.stereotype.Repository;
@Repository
public interface InvoiceMapper extends BaseMapper<Invoice> {
}

View File

@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.business.domain.api.mabang.getorderlist.Order;
import org.jeecg.modules.business.entity.PlatformOrder;
import org.jeecg.modules.business.entity.PlatformOrderShopSync;
import org.jeecg.modules.business.vo.OrderKpi;
import org.jeecg.modules.business.vo.ShippingFeeBillableOrders;
import org.jeecg.modules.business.vo.clientPlatformOrder.ClientPlatformOrderPage;
import org.jeecg.modules.business.vo.clientPlatformOrder.section.OrderQuantity;
@ -13,6 +14,7 @@ import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @Description:
@ -214,4 +216,7 @@ public interface PlatformOrderMapper extends BaseMapper<PlatformOrder> {
List<PlatformOrder> getPlatformOrdersByInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
OrderKpi countPlatformOrders(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end, @Param("showAllData") boolean showAllData, @Param("username") String username);
Map<String, String> fetchShippingPeriodAndType(@Param("invoiceNumber") String invoiceNumber);
}

View File

@ -1,12 +1,16 @@
package org.jeecg.modules.business.mapper;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.business.entity.Invoice;
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.InvoiceKpi;
import org.jeecg.modules.business.vo.InvoiceMetaData;
import org.jeecg.modules.business.vo.PurchaseOrderPage;
import org.jeecg.modules.business.vo.SkuQuantity;
@ -113,9 +117,16 @@ public interface PurchaseOrderMapper extends BaseMapper<PurchaseOrder> {
InvoiceMetaData getMetaDataFromInvoiceNumbers(@Param("invoiceNumber") String invoiceNumber);
List<PurchaseOrderPage> getPage(@Param("offset") long offset, @Param("size") long size);
List<PurchaseOrderPage> getPage(@Param("offset") long offset, @Param("size") long size, @Param("clientId") String clientId);
long countPurchaseOrders();
void updatePurchaseOrderStatus(@Param("invoiceNumber") String invoiceNumber, @Param("isOrdered") boolean isOrdered);
InvoiceKpi countPurchaseInvoices(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end, @Param("showAllData") boolean showAllData, @Param("username") String username);
void setPaid(@Param("invoiceNumbers") List<String> invoiceNumbers);
PurchaseOrder getPurchaseByInvoiceNumberAndClientId(@Param("invoiceNumber") String invoiceNumber, @Param("clientId") String clientId);
List<PurchaseOrder> getPurchasesByInvoices(@Param("invoices") List<Invoice> invoices);
}

View File

@ -17,6 +17,6 @@ import java.util.List;
@Repository
public interface SavRefundWithDetailMapper extends BaseMapper<SavRefundWithDetail> {
List<SavRefundWithDetail> findUnprocessedRefundsByClient(@Param("clientId") String clientId);
List<SavRefundWithDetail> fetchRefundsWhere(@Param("shop") String shop, @Param("orderID") String orderID, @Param("column") String column, @Param("order") String order);
List<SavRefundWithDetail> fetchRefundsWhere(@Param("shop") String shop, @Param("orderID") String orderID, @Param("invoiceNumber") String invoiceNumber, @Param("column") String column, @Param("order") String order);
List<SavRefundWithDetail> getRefundsByInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
}

View File

@ -3,9 +3,13 @@ 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.*;
import org.jeecg.modules.business.vo.InvoiceKpi;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* @Description:
@ -21,4 +25,7 @@ public interface ShippingInvoiceMapper extends BaseMapper<ShippingInvoice> {
List<PlatformOrderContent> fetchPlatformOrderContent(@Param("platformOrderId") String platformOrderId);
Client fetchShopOwnerFromInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
Currency fetchInvoiceCurrencyByCode(@Param("invoiceNumber") String invoiceCode);
InvoiceKpi countShippingInvoices(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end, @Param("showAllData") boolean showAllData, @Param("username") String username);
void setPaid(@Param("invoiceNumbers") List<String> invoiceNumbers);
Client getClientByInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
}

View File

@ -57,4 +57,12 @@ public interface SkuMapper extends BaseMapper<Sku> {
List<SkuOrderPage> fetchSkusByClient(@Param("clientId") String clientId);
String getIdFromErpCode(@Param("erpCode") String erpCode);
List<Sku> listSkus();
Sku getByErpCode(@Param("erpCode") String erpCode);
void updateBatchStockByIds(@Param("skus") List<Sku> skuToUpdate);
List<SkuOrderPage> getInventoryByInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
}

View File

@ -8,5 +8,15 @@
WHERE original_currency = #{original}
AND target_currency = #{target}
ORDER BY effective_date DESC
LIMIT 1 </select>
LIMIT 1;
</select>
<select id="getExchangeRateFromDate" resultType="java.math.BigDecimal">
SELECT rate
FROM exchange_rates
WHERE original_currency = #{original}
AND target_currency = #{target}
AND effective_date &lt;= #{datetime}
ORDER BY effective_date DESC
LIMIT 1;
</select>
</mapper>

View File

@ -163,6 +163,7 @@
AND shipping_time >= #{begin}
AND #{end} >= shipping_time
AND shipping_invoice_number IS NULL
AND erp_status = 3;
</select>
<select id="findUninvoicedOrdersForShopsAndStatus" resultType="org.jeecg.modules.business.entity.PlatformOrder">
SELECT *
@ -839,4 +840,43 @@
FROM platform_order
WHERE shipping_invoice_number = #{invoiceNumber};
</select>
<select id="countPlatformOrders" resultType="org.jeecg.modules.business.vo.OrderKpi">
WITH processedByUser AS (
SELECT invoice_number
FROM shipping_invoice si
WHERE create_by = #{username}
UNION ALL
SELECT invoice_number
FROM purchase_order po
WHERE create_by = #{username}
), processed AS (
SELECT COUNT(*) AS processed
FROM platform_order
WHERE erp_status = 4
AND IF(#{showAllData} = true,
shipping_invoice_number IS NOT NULL,
(shipping_invoice_number IN (SELECT invoice_number FROM processedByUser) OR purchase_invoice_number IN (SELECT invoice_number FROM processedByUser))
)
AND shipping_time BETWEEN #{start} AND #{end}
), processing AS (
SELECT COUNT(*) AS processing
FROM platform_order
WHERE erp_status IN (1,2,3)
AND order_time &lt;= #{end}
AND IF(#{showAllData} = true,
shipping_invoice_number IS NOT NULL,
shipping_invoice_number IN (SELECT invoice_number FROM processedByUser) OR purchase_invoice_number IN (SELECT invoice_number FROM processedByUser)
)
)
SELECT processed, processing
FROM processed, processing;
</select>
<select id="fetchShippingPeriodAndType" resultType="java.util.Map">
SELECT DATE_FORMAT(IF(po.shipping_time &lt; s.create_time, MIN(po.shipping_time), MIN(po.order_time)),'%Y-%m-%d %T') AS startDate,
DATE_FORMAT(IF(po.shipping_time &lt; s.create_time, MAX(po.shipping_time), MAX(po.order_time)),'%Y-%m-%d %T') AS endDate,
IF(po.shipping_time &lt; s.create_time, 'post-shipping', 'pre-shipping') AS type
FROM platform_order po
JOIN shipping_invoice s ON po.shipping_invoice_number = s.invoice_number
WHERE shipping_invoice_number = #{invoiceNumber};
</select>
</mapper>

View File

@ -50,6 +50,7 @@
FROM purchase_order
WHERE MONTH(create_time) = MONTH(CURRENT_DATE())
AND YEAR(create_time) = YEAR(CURRENT_DATE())
AND invoice_number LIKE '%%%%-%%-1%%%'
ORDER BY create_time DESC
LIMIT 1
</select>
@ -126,6 +127,7 @@
FROM purchase_order po
LEFT JOIN platform_order p
ON p.purchase_invoice_number = po.invoice_number
WHERE IF(#{clientId} IS NOT NULL, po.client_id = #{clientId}, true)
GROUP BY po.id, po.create_time
ORDER BY po.create_time DESC
LIMIT #{size} OFFSET #{offset}
@ -139,4 +141,48 @@
SET ordered = #{isOrdered}
WHERE invoice_number = #{invoiceNumber}
</update>
<select id="countPurchaseInvoices" resultType="org.jeecg.modules.business.vo.InvoiceKpi">
WITH purchaseEur AS (
SELECT IFNULL(count(*),0) as qty, IFNULL(sum(final_amount),0) as total
FROM purchase_order
WHERE create_time BETWEEN #{start} AND #{end}
AND currency_id = (SELECT id FROM currency WHERE code = 'EUR')
AND IF(#{showAllData} = false, create_by = #{username}, true)
), usdExchangeRate AS (
SELECT rate
FROM exchange_rates
WHERE original_currency = 'EUR' AND target_currency = 'USD'
ORDER BY create_time DESC LIMIT 1
), purchaseUsd AS (
SELECT IFNULL(count(*),0) as qty, IFNULL(sum(final_amount/(SELECT rate FROM usdExchangeRate)),0) as total
FROM purchase_order
WHERE create_time BETWEEN #{start} AND #{end}
AND currency_id = (SELECT id FROM currency WHERE code = 'USD')
AND IF(#{showAllData} = false, create_by = #{username}, true)
)
SELECT (purchaseEur.qty + purchaseUsd.qty) as qty, ROUND(purchaseEur.total + purchaseUsd.total, 2) as total
FROM purchaseEur, usdExchangeRate, purchaseUsd
</select>
<update id="setPaid">
UPDATE purchase_order
SET paid_amount = final_amount
WHERE invoice_number IN
<foreach collection="invoiceNumbers" item="invoiceNumber" index="index" open="(" separator="," close=")">
#{invoiceNumber}
</foreach>
</update>
<select id="getPurchaseByInvoiceNumberAndClientId" resultType="org.jeecg.modules.business.entity.PurchaseOrder">
SELECT *
FROM purchase_order
WHERE invoice_number = #{invoiceNumber}
AND client_id = #{clientId}
</select>
<select id="getPurchasesByInvoices" resultType="org.jeecg.modules.business.entity.PurchaseOrder">
SELECT *
FROM purchase_order
WHERE id IN
<foreach collection="invoices" item="invoice" index="index" open="(" separator="," close=")">
#{invoice.id}
</foreach>
</select>
</mapper>

View File

@ -18,6 +18,12 @@
JOIN shop s ON po.shop_id = s.id
WHERE s.erp_code LIKE #{shop}
AND po.platform_order_id LIKE #{orderID}
AND (CASE
WHEN #{invoiceNumber} = 'null' THEN invoice_number IS NULL
WHEN #{invoiceNumber} = 'notNull' THEN invoice_number IS NOT NULL
WHEN #{invoiceNumber} = '%%' THEN true
ELSE invoice_number LIKE #{invoiceNumber}
END)
ORDER BY ${column} ${order};
</select>

View File

@ -35,4 +35,40 @@
JOIN currency c ON s.currency_id = c.id
WHERE invoice_number = #{invoiceNumber}
</select>
<select id="countShippingInvoices" resultType="org.jeecg.modules.business.vo.InvoiceKpi">
WITH shippingEur AS (
SELECT IFNULL(count(*),0) as qty, IFNULL(sum(final_amount),0) as total
FROM shipping_invoice
WHERE create_time BETWEEN #{start} AND #{end}
AND currency_id = (SELECT id FROM currency WHERE code = 'EUR')
AND IF(#{showAllData} = false, create_by = #{username}, true)
), usdExchangeRate AS (
SELECT rate
FROM exchange_rates
WHERE original_currency = 'EUR' AND target_currency = 'USD'
ORDER BY create_time DESC LIMIT 1
), shippingUsd AS (
SELECT IFNULL(count(*),0) as qty, IFNULL(sum(final_amount/(SELECT rate FROM usdExchangeRate)),0) as total
FROM shipping_invoice
WHERE create_time BETWEEN #{start} AND #{end}
AND currency_id = (SELECT id FROM currency WHERE code = 'USD')
AND IF(#{showAllData} = false, create_by = #{username}, true)
)
SELECT (shippingEur.qty + shippingUsd.qty) as qty, ROUND(shippingEur.total + shippingUsd.total, 2) as total
FROM shippingEur, usdExchangeRate, shippingUsd
</select>
<update id="setPaid">
UPDATE shipping_invoice
SET paid_amount = final_amount
WHERE invoice_number IN
<foreach collection="invoiceNumbers" item="invoiceNumber" open="(" separator="," close=")">
#{invoiceNumber}
</foreach>
</update>
<select id="getClientByInvoiceNumber" resultType="org.jeecg.modules.business.entity.Client">
SELECT c.*
FROM client c
JOIN shipping_invoice s ON s.client_id = c.id
WHERE s.invoice_number = #{invoiceNumber}
</select>
</mapper>

View File

@ -166,6 +166,7 @@
)
SELECT s.id,
s.erp_code,
p.en_name as productEn,
p.zh_name as product,
s.purchasing_amount,
s.available_amount,
@ -207,11 +208,112 @@
LEFT JOIN sales_42 s42 ON s.id = s42.sku_id
LEFT JOIN sales_7 s7 ON s.id = s7.sku_id
LEFT JOIN qtyInOrdersNotShipped ON s.id = qtyInOrdersNotShipped.ID
WHERE cs.client_id = #{clientId};
WHERE cs.client_id = #{clientId}
</select>
<select id="getInventoryByInvoiceNumber" resultType="org.jeecg.modules.business.vo.SkuOrderPage">
WITH skusFromInvoice AS (
SELECT sku_id, quantity
FROM purchase_order_sku pos
JOIN purchase_order po ON pos.purchase_order_id = po.id
WHERE po.invoice_number = #{invoiceNumber}
),
qtyInOrdersNotShipped AS (
SELECT sku_id as ID, SUM(quantity) AS quantity
FROM platform_order_content poc
JOIN platform_order po ON poc.platform_order_id = po.id
JOIN shop s ON po.shop_id = s.id
JOIN client c ON s.owner_id = c.id
WHERE sku_id IN (SELECT sku_id FROM skusFromInvoice)
AND po.erp_status IN ('1','2')
AND poc.erp_status IN ('1','2')
GROUP BY sku_id
)
SELECT s.id,
s.erp_code,
p.en_name as productEn,
p.zh_name as product,
sfi.quantity as qtyOrdered,
s.purchasing_amount as purchasing_amount,
s.available_amount as available_amount,
IFNULL(qtyInOrdersNotShipped.quantity, 0) as qtyInOrdersNotShipped,
IF(s.available_amount IS NULL, 0, s.available_amount)
+ IF(s.purchasing_amount IS NULL, 0, s.purchasing_amount)
- IF(qtyInOrdersNotShipped.quantity IS NULL, 0, qtyInOrdersNotShipped.quantity)
as stock,
s.image_source as image_source,
s.service_fee as service_fee,
IF(sp.price_rmb IS NULL, sp.price,
(
ROUND(
sp.price_rmb /
(SELECT rate
FROM exchange_rates
WHERE original_currency = 'EUR' AND target_currency = 'RMB'
ORDER BY create_time DESC LIMIT 1)
,2)
)
) as sku_price,
sp.threshold as discount_moq,
IF(sp.price_rmb IS NULL, sp.discounted_price,
(
ROUND(
sp.discounted_price_rmb /
(SELECT rate
FROM exchange_rates
WHERE target_currency = 'EUR' AND original_currency = 'RMB'
ORDER BY create_time DESC LIMIT 1)
,2)
)
) as discounted_price,
IFNULL(s7.quantity, 0) as sales_last_week,
IFNULL(s28.quantity, 0) as sales_four_weeks,
IFNULL(s42.quantity, 0) as sales_six_weeks
FROM sku s
JOIN product p ON s.product_id = p.id
LEFT JOIN sku_price sp ON s.id = sp.sku_id
LEFT JOIN sales_28 s28 ON s.id = s28.sku_id
LEFT JOIN sales_42 s42 ON s.id = s42.sku_id
LEFT JOIN sales_7 s7 ON s.id = s7.sku_id
LEFT JOIN qtyInOrdersNotShipped ON s.id = qtyInOrdersNotShipped.ID
JOIN skusFromInvoice sfi ON s.id = sfi.sku_id
WHERE s.id IN (SELECT sku_id FROM skusFromInvoice)
GROUP BY s.id;
</select>
<select id="getIdFromErpCode" resultType="java.lang.String">
SELECT id
FROM sku
WHERE erp_code = #{erpCode};
</select>
<select id="listSkus" resultType="org.jeecg.modules.business.entity.Sku">
SELECT *
FROM sku
JOIN client_sku cs ON sku.id = cs.sku_id
JOIN client c ON cs.client_id = c.id
WHERE c.active = 1;
</select>
<select id="getByErpCode" resultType="org.jeecg.modules.business.entity.Sku">
SELECT * FROM sku
WHERE erp_code = #{erpCode};
</select>
<update id="updateBatchStockByIds">
UPDATE sku
SET update_by = 'mabang api',
update_time = NOW(),
available_amount =
case id
<foreach collection="skus" item="sku" index="index" separator=" ">
when #{sku.id} then #{sku.availableAmount}
</foreach>
end,
purchasing_amount =
case id
<foreach collection="skus" item="sku" index="index" separator=" ">
when #{sku.id} then #{sku.purchasingAmount}
</foreach>
end
WHERE id IN
<foreach collection="skus" item="sku" index="index" separator="," open="(" close=")">
#{sku.id}
</foreach>
</update>
</mapper>

View File

@ -0,0 +1,9 @@
package org.jeecg.modules.business.service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
public interface DashboardService {
Map<String,?> getKpis(LocalDateTime start, LocalDateTime end, List<String> roles, String username);
}

View File

@ -106,6 +106,8 @@ public interface IPlatformOrderService extends IService<PlatformOrder> {
* @return list of pre-shipping orders and their contents
*/
Map<PlatformOrder, List<PlatformOrderContent>> fetchOrderData(List<String> orderIds);
Map<PlatformOrder, List<PlatformOrderContent>> fetchOrderDataByInvoiceCode(String invoiceCode);
/**
* Find previous invoice code
@ -239,4 +241,6 @@ public interface IPlatformOrderService extends IService<PlatformOrder> {
List<ShippingFeeBillableOrders> fetchShippingFeeBillableOrders();
List<PlatformOrder> getPlatformOrdersByInvoiceNumber(String invoiceNumber);
Map<String, String> fetchShippingPeriodAndType(String invoiceNumber);
}

View File

@ -4,8 +4,10 @@ import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.business.domain.api.mabang.purDoGetProvider.ProviderData;
import org.jeecg.modules.business.vo.InvoiceMetaData;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
public interface IProviderMabangService extends IService<ProviderData> {
/**
@ -15,5 +17,5 @@ public interface IProviderMabangService extends IService<ProviderData> {
*/
void saveProviderFromMabang(List<ProviderData> providerDataList);
boolean addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData);
boolean addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData, AtomicReference<Map<String, LocalDateTime>> providersHistory);
}

View File

@ -46,12 +46,6 @@ public interface IPurchaseOrderService extends IService<PurchaseOrder> {
*/
public void delBatchMain(Collection<? extends Serializable> idList);
@Transactional
void cancelInvoice(String purchaseId, String invoiceNumber);
@Transactional
void cancelBatchInvoice(String ids);
/**
* Set purchase orders to the page indicated by argument.
*
@ -112,7 +106,7 @@ public interface IPurchaseOrderService extends IService<PurchaseOrder> {
* @return the file in binary
* @throws IOException IO error while reading the file.
*/
InvoiceMetaData makeInvoice(String purchaseID) throws IOException, URISyntaxException;
InvoiceMetaData makeInvoice(String purchaseID) throws IOException, URISyntaxException, UserException;
byte[] getInvoiceByte(String invoiceCode) throws IOException;
@ -134,7 +128,15 @@ public interface IPurchaseOrderService extends IService<PurchaseOrder> {
InvoiceMetaData getMetaDataFromInvoiceNumbers(String invoiceNumber);
void setPageForList(Page<PurchaseOrderPage> page);
void setPageForList(Page<PurchaseOrderPage> page, String clientId);
void updatePurchaseOrderStatus(String invoiceNumber, boolean isOrdered);
void setPaid(List<String> invoiceNumbers);
void deleteInvoice(String invoiceNumber);
PurchaseOrder getPurchaseByInvoiceNumberAndClientId(String invoiceNumber, String clientId);
List<PurchaseOrder> getPurchasesByInvoices(List<Invoice> invoices);
}

View File

@ -13,6 +13,6 @@ import java.util.List;
*/
public interface ISavRefundWithDetailService extends IService<SavRefundWithDetail> {
List<SavRefundWithDetail> findUnprocessedRefundsByClient(String clientId);
List<SavRefundWithDetail> fetchRefundsWhere(String shop, String orderID, String column, String order);
List<SavRefundWithDetail> fetchRefundsWhere(String shop, String orderID, String invoiceNumber, String column, String order);
List<SavRefundWithDetail> getRefundsByInvoiceNumber(String invoiceNumber);
}

View File

@ -45,6 +45,7 @@ public interface IShippingInvoiceService extends IService<ShippingInvoice> {
// Utils
public List<Path> getPath(String dirPath, String invoiceNumber);
public List<Path> getPath(String dirPath, String invoiceNumber, String invoiceEntity);
public String getInvoiceList(String invoiceNumber, String filetype);
boolean deleteAttachmentFile(String filename);
void setPaid(List<String> invoiceNumbers);
}

View File

@ -20,7 +20,7 @@ import java.util.Map;
* @Version: V1.1
*/
public interface ISkuService extends IService<Sku> {
List<Sku> listSkus();
List<Sku> selectByMainId(String mainId);
/**
@ -100,4 +100,7 @@ public interface ISkuService extends IService<Sku> {
void addSkuQuantity(Map<String, Integer> quantityPurchased);
String getIdFromErpCode(String erpCode);
Sku getByErpCode(String erpCode);
void updateBatchStockByIds(List<Sku> skuToUpdate);
List<SkuOrderPage> getInventoryByInvoiceNumber(String invoiceNumber);
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.business.entity.Invoice;
import java.util.List;
public interface InvoiceService extends IService<Invoice> {
boolean cancelInvoice(String id, String invoiceNumber, String clientId);
boolean cancelBatchInvoice(List<Invoice> invoices);
}

View File

@ -8,13 +8,12 @@ import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.business.controller.UserException;
import org.jeecg.modules.business.domain.excel.SheetManager;
import org.jeecg.modules.business.domain.purchase.invoice.PurchaseInvoice;
import org.jeecg.modules.business.domain.shippingInvoice.CompleteInvoice;
import org.jeecg.modules.business.domain.shippingInvoice.ShippingInvoice;
import org.jeecg.modules.business.domain.shippingInvoice.ShippingInvoiceFactory;
import org.jeecg.modules.business.entity.Client;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.entity.ClientCategory.CategoryName;
import org.jeecg.modules.business.entity.PlatformOrder;
import org.jeecg.modules.business.entity.SavRefundWithDetail;
import org.jeecg.modules.business.mapper.*;
import org.jeecg.modules.business.vo.*;
import org.jetbrains.annotations.NotNull;
@ -45,6 +44,8 @@ import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.jeecg.modules.business.entity.Invoice.InvoiceType.*;
@Service
@Slf4j
public class PlatformOrderShippingInvoiceService {
@ -107,11 +108,16 @@ public class PlatformOrderShippingInvoiceService {
@Value("${jeecg.path.completeTemplatePath_US}")
private String COMPLETE_INVOICE_TEMPLATE_US;
@Value("${jeecg.path.purchaseTemplatePath}")
private String PURCHASE_INVOICE_TEMPLATE;
@Value("${jeecg.path.shippingInvoiceDir}")
private String INVOICE_DIR;
@Value("${jeecg.path.purchaseInvoiceDir}")
private String PURCHASE_INVOICE_DIR;
@Value("${jeecg.path.purchaseInventoryDir}")
private String PURCHASE_INVENTORY_DIR;
@Value("${jeecg.path.shippingInvoiceDetailDir}")
private String INVOICE_DETAIL_DIR;
@ -153,11 +159,25 @@ public class PlatformOrderShippingInvoiceService {
"Montant total du remboursement",
"N° de facture"
};
private final static String[] PURCHASE_INVENTORY_TITLES = {
"SKU",
"Nom Anglais",
"Nom Chinois",
"Stock disponible",
"Achat WIA en cours",
"Cmd en cours",
"Stock " + (new SimpleDateFormat("dd/MM").format(new Date())),
"Quantité à acheter",
"Ventes 7j",
"Ventes 28j",
"Ventes 42j",
"Prix à l'unité",
};
public Period getValidPeriod(List<String> shopIDs) {
Date begin = platformOrderMapper.findEarliestUninvoicedPlatformOrder(shopIDs);
Date end = platformOrderMapper.findLatestUninvoicedPlatformOrder(shopIDs);
return new Period(begin, end);
return new Period(begin, end, "");
}
public Period getValidOrderTimePeriod(List<String> shopIDs, List<Integer> erpStatuses) {
Date begin = platformOrderMapper.findEarliestUninvoicedPlatformOrderTime(shopIDs, erpStatuses);
@ -170,7 +190,7 @@ public class PlatformOrderShippingInvoiceService {
Date end = platformOrderMapper.findLatestUninvoicedPlatformOrderTime(shopIDs, erpStatuses);
ldt = LocalDateTime.ofInstant(end.toInstant(), shanghai);
Date endZoned = Date.from(ldt.atZone(paris).toInstant());
return new Period(beginZoned, endZoned);
return new Period(beginZoned, endZoned, "");
}
public List<String> getShippingOrderIdBetweenDate(List<String> shops, String start, String end, List<String> wareHouses) {
List<PlatformOrder> orders = platformOrderMapper.fetchUninvoicedShippedOrderIDInShops( start, end, shops, wareHouses);
@ -204,7 +224,7 @@ public class PlatformOrderShippingInvoiceService {
param.getBalance()
);
// Chooses invoice template based on client's preference on currency
return getInvoiceMetaData(username, invoice);
return getInvoiceMetaDataAndInsert(username, invoice);
}
/**
@ -227,7 +247,7 @@ public class PlatformOrderShippingInvoiceService {
String username = ((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername();
// Creates invoice by factory
ShippingInvoice invoice = factory.createShippingInvoice(param.clientID(), param.orderIds(), param.getType(), param.getStart(), param.getEnd());
return getInvoiceMetaData(username, invoice);
return getInvoiceMetaDataAndInsert(username, invoice);
}
/**
@ -250,7 +270,7 @@ public class PlatformOrderShippingInvoiceService {
String username = ((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername();
// Creates invoice by factory
CompleteInvoice invoice = factory.createCompleteShippingInvoice(username, param.clientID(), null, param.orderIds(), param.getType(), param.getStart(), param.getEnd());
return getInvoiceMetaData(username, invoice);
return getInvoiceMetaDataAndInsert(username, invoice);
}
/**
@ -282,10 +302,27 @@ public class PlatformOrderShippingInvoiceService {
List<String> orderIds = platformOrderList.stream().map(PlatformOrder::getId).collect(Collectors.toList());
// Creates invoice by factory
CompleteInvoice invoice = factory.createCompleteShippingInvoice(username, param.clientID(), param.getBalance() ,orderIds, method, param.getStart(), param.getEnd());
return getInvoiceMetaData(username, invoice);
return getInvoiceMetaDataAndInsert(username, invoice);
}
@NotNull
private InvoiceMetaData getInvoiceMetaData(String username, ShippingInvoice invoice) throws IOException {
private InvoiceMetaData getInvoiceMetaDataAndInsert(String username, ShippingInvoice invoice) throws IOException {
InvoiceMetaData invoiceMetaData = getInvoiceMetaData(invoice);
String currencyId = currencyService.getIdByCode(invoice.client().getCurrency());
// save to DB
org.jeecg.modules.business.entity.ShippingInvoice shippingInvoiceEntity = org.jeecg.modules.business.entity.ShippingInvoice.of(
username,
invoice.client().getId(),
invoice.code(),
invoice.getTotalAmount(),
invoice.reducedAmount(),
invoice.paidAmount(),
currencyId
);
shippingInvoiceMapper.insert(shippingInvoiceEntity);
return invoiceMetaData;
}
@NotNull
private InvoiceMetaData getInvoiceMetaData(ShippingInvoice invoice) throws IOException {
// Chooses invoice template based on client's preference on currency
Path src;
if (invoice instanceof CompleteInvoice) {
@ -307,18 +344,20 @@ public class PlatformOrderShippingInvoiceService {
Path out = Paths.get(INVOICE_DIR, filename);
Files.copy(src, out, StandardCopyOption.REPLACE_EXISTING);
invoice.toExcelFile(out);
String currencyId = currencyService.getIdByCode(invoice.client().getCurrency());
// save to DB
org.jeecg.modules.business.entity.ShippingInvoice shippingInvoiceEntity = org.jeecg.modules.business.entity.ShippingInvoice.of(
username,
invoice.client().getId(),
invoice.code(),
invoice.getTotalAmount(),
invoice.reducedAmount(),
invoice.paidAmount(),
currencyId
);
shippingInvoiceMapper.insert(shippingInvoiceEntity);
return new InvoiceMetaData(filename, invoice.code(), invoice.client().getInternalCode(), invoice.client().getInvoiceEntity(), "");
}
@NotNull
private InvoiceMetaData getInvoiceMetaData(PurchaseInvoice invoice) throws IOException {
// Chooses invoice template based on client's preference on currency
Path template = Paths.get(PURCHASE_INVOICE_TEMPLATE );
// Writes invoice content to a new excel file
String filename = "Invoice N°" + invoice.code() + " (" + invoice.client().getInvoiceEntity() + ").xlsx";
Path out = Paths.get(PURCHASE_INVOICE_DIR, filename);
Files.copy(template, out, StandardCopyOption.REPLACE_EXISTING);
invoice.toExcelFile(out);
return new InvoiceMetaData(filename, invoice.code(), invoice.client().getInternalCode(), invoice.client().getInvoiceEntity(), "");
}
@ -471,6 +510,56 @@ public class PlatformOrderShippingInvoiceService {
return Files.readAllBytes(target);
}
public byte[] exportPurchaseInventoryToExcel(List<SkuOrderPage> skuOrders, InvoiceMetaData metaData) throws IOException {
SheetManager sheetManager = SheetManager.createXLSX();
sheetManager.startDetailsSheet();
for (String title : PURCHASE_INVENTORY_TITLES) {
sheetManager.write(title);
sheetManager.nextCol();
}
sheetManager.moveCol(0);
sheetManager.nextRow();
for (SkuOrderPage skuPurchase : skuOrders) {
sheetManager.write(skuPurchase.getErpCode());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getProductEn());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getProduct());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getAvailableAmount());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getPurchasingAmount());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getQtyInOrdersNotShipped());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getStock());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getQtyOrdered());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getSalesLastWeek());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getSalesFourWeeks());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getSalesSixWeeks());
sheetManager.nextCol();
sheetManager.write(skuPurchase.getSkuPrice());
sheetManager.moveCol(0);
sheetManager.nextRow();
}
Path target = Paths.get(PURCHASE_INVENTORY_DIR, metaData.getInternalCode() + "_(" + metaData.getInvoiceEntity() + ")_" + metaData.getInvoiceCode() + "_Inventaire_SKU.xlsx");
int i = 2;
while (Files.exists(target)) {
target = Paths.get(PURCHASE_INVENTORY_DIR, metaData.getInternalCode() + "_(" + metaData.getInvoiceEntity() + ")_" + metaData.getInvoiceCode() + "_Inventaire_SKU_(" + i + ").xlsx");
i++;
}
Files.createFile(target);
sheetManager.export(target);
sheetManager.getWorkbook().close();
System.gc();
return Files.readAllBytes(target);
}
/**
* make shipping invoice by client and type (shipping or complete)
@ -637,12 +726,15 @@ public class PlatformOrderShippingInvoiceService {
* @param filetype if it's an invoice or invoice detail
* @return String returns the path of the invoice file
*/
public String getInvoiceList(String invoiceNumber, String filetype) {
public String getInvoiceList(String invoiceNumber, String filetype) throws UserException, IOException {
log.info("Invoice number : " + invoiceNumber);
List<Path> pathList = new ArrayList<>();
if(filetype.equals("invoice")) {
log.info("File asked is of type invoice");
pathList = getPath(INVOICE_DIR, invoiceNumber);
if(Invoice.getType(invoiceNumber).equalsIgnoreCase(PURCHASE.name()))
pathList = getPath(PURCHASE_INVOICE_DIR, invoiceNumber);
else
pathList = getPath(INVOICE_DIR, invoiceNumber);
}
if(filetype.equals("detail")) {
log.info("File asked is of type invoice detail");
@ -650,14 +742,17 @@ public class PlatformOrderShippingInvoiceService {
}
if(pathList.isEmpty()) {
log.error("NO INVOICE FILE FOUND : " + invoiceNumber);
return "ERROR";
}
else {
for (Path path : pathList) {
log.info(path.toString());
log.info("Generating a new invoice file ...");
if(filetype.equals("invoice"))
pathList = generateInvoiceExcel(invoiceNumber, filetype);
else {
return "ERROR";
}
return pathList.get(0).toString();
}
for (Path path : pathList) {
log.info(path.toString());
}
return pathList.get(0).toString();
}
public String convertToPdf(String invoiceNumber, String fileType) throws Exception {
String excelFilePath = getInvoiceList(invoiceNumber, fileType);// (C:\PATH\filename.xlsx)
@ -712,4 +807,40 @@ public class PlatformOrderShippingInvoiceService {
log.info("Zipping done ...");
return zipFilename;
}
public List<Path> generateInvoiceExcel(String invoiceNumber, String filetype) throws UserException, IOException {
ShippingInvoiceFactory factory = new ShippingInvoiceFactory(
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService, emailService, env);
Path out = null;
if(filetype.equals("invoice")) {
if(Invoice.getType(invoiceNumber).equalsIgnoreCase(PURCHASE.name())) {
PurchaseInvoice invoice = factory.buildExistingPurchaseInvoice(invoiceNumber);
InvoiceMetaData invoiceMetaData = getInvoiceMetaData(invoice);
String filename = "Invoice N°" + invoice.code() + " (" + invoice.client().getInvoiceEntity() + ").xlsx";
out = Paths.get(PURCHASE_INVOICE_DIR, filename);
}
if(Invoice.getType(invoiceNumber).equalsIgnoreCase(SHIPPING.name())) {
Client client = shippingInvoiceMapper.getClientByInvoiceNumber(invoiceNumber);
Map<String, String> period = platformOrderService.fetchShippingPeriodAndType(invoiceNumber);
String clientId = client.getId();
ShippingInvoice invoice = factory.buildExistingShippingInvoice(invoiceNumber, clientId, period.get("startDate"), period.get("endDate"), filetype, period.get("type"));
InvoiceMetaData invoiceMetaData = getInvoiceMetaData(invoice);
String filename = "Invoice N°" + invoice.code() + " (" + invoice.client().getInvoiceEntity() + ").xlsx";
out = Paths.get(INVOICE_DIR, filename);
}
if(Invoice.getType(invoiceNumber).equalsIgnoreCase(COMPLETE.name())) {
Client client = shippingInvoiceMapper.getClientByInvoiceNumber(invoiceNumber);
Map<String, String> period = platformOrderService.fetchShippingPeriodAndType(invoiceNumber);
String clientId = client.getId();
CompleteInvoice invoice = factory.buildExistingCompleteInvoice(invoiceNumber, clientId, period.get("startDate"), period.get("endDate"), filetype, period.get("type"));
InvoiceMetaData invoiceMetaData = getInvoiceMetaData(invoice);
String filename = "Invoice N°" + invoice.code() + " (" + invoice.client().getInvoiceEntity() + ").xlsx";
out = Paths.get(INVOICE_DIR, filename);
}
}
return Collections.singletonList(out);
}
}

View File

@ -48,19 +48,36 @@ public class BalanceServiceImpl extends ServiceImpl<BalanceMapper, Balance> impl
@Override
public void updateBalance(String clientId, String invoiceCode, String invoiceType) {
// balance update
ShippingInvoice invoice = shippingInvoiceService.getShippingInvoice(invoiceCode);
String currency = currencyService.getCodeById(invoice.getCurrencyId());
BigDecimal previousBalance = getBalanceByClientIdAndCurrency(clientId, currency);
BigDecimal currentBalance = previousBalance.subtract(invoice.getFinalAmount());
if(invoiceType.equals("shipping")) {
ShippingInvoice invoice = shippingInvoiceService.getShippingInvoice(invoiceCode);
String currency = currencyService.getCodeById(invoice.getCurrencyId());
BigDecimal previousBalance = getBalanceByClientIdAndCurrency(clientId, currency);
BigDecimal currentBalance = previousBalance.subtract(invoice.getFinalAmount());
SysUser sysUser = new SysUser();
Balance balance = Balance.of(sysUser.getUsername(), clientId, invoice.getCurrencyId(), Balance.OperationType.Debit.name(), invoice.getId(), currentBalance);
balanceMapper.insert(balance);
}
if(invoiceType.equals("complete")) {
ShippingInvoice invoice = shippingInvoiceService.getShippingInvoice(invoiceCode);
String currency = currencyService.getCodeById(invoice.getCurrencyId());
BigDecimal previousBalance = getBalanceByClientIdAndCurrency(clientId, currency);
BigDecimal currentBalance = previousBalance.subtract(invoice.getFinalAmount());
BigDecimal purchaseFees = purchaseOrderService.getPurchaseFeesByInvoiceCode(invoiceCode);
currentBalance = currentBalance.subtract(purchaseFees);
SysUser sysUser = new SysUser();
Balance balance = Balance.of(sysUser.getUsername(), clientId, invoice.getCurrencyId(), Balance.OperationType.Debit.name(), invoice.getId(), currentBalance);
balanceMapper.insert(balance);
}
if(invoiceType.equals("purchase")) {
PurchaseOrder invoice = purchaseOrderService.getPurchaseByInvoiceNumber(invoiceCode);
String currency = currencyService.getCodeById(invoice.getCurrencyId());
BigDecimal previousBalance = getBalanceByClientIdAndCurrency(clientId, currency);
BigDecimal currentBalance = previousBalance.subtract(invoice.getFinalAmount());
SysUser sysUser = new SysUser();
Balance balance = Balance.of(sysUser.getUsername(), clientId, invoice.getCurrencyId(), Balance.OperationType.Debit.name(), invoice.getId(), currentBalance);
balanceMapper.insert(balance);
}
SysUser sysUser = new SysUser();
Balance balance = Balance.of(sysUser.getUsername(), clientId, invoice.getCurrencyId(), Balance.OperationType.Debit.name(), invoice.getId(), currentBalance);
balanceMapper.insert(balance);
}
@Override

View File

@ -55,7 +55,7 @@ public class ClientSkuServiceImpl extends ServiceImpl<ClientSkuMapper, ClientSku
unknownSkuErpCode.add(erpCode);
continue;
}
log.info("Associating sku \"{}\" with client \"{}\". ", sku.getErpCode(), clientId);
log.info("Associating sku \"{}\" with client \"{}\" : \"{}\". ", sku.getErpCode(),internalCode, clientId);
addClientSku(clientId, sku.getId());
}
return unknownSkuErpCode;

View File

@ -0,0 +1,78 @@
package org.jeecg.modules.business.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.mapper.PlatformOrderMapper;
import org.jeecg.modules.business.mapper.PurchaseOrderMapper;
import org.jeecg.modules.business.mapper.ShippingInvoiceMapper;
import org.jeecg.modules.business.service.DashboardService;
import org.jeecg.modules.business.vo.InvoiceKpi;
import org.jeecg.modules.business.vo.Kpi;
import org.jeecg.modules.business.vo.OrderKpi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Slf4j
@Service
public class DashboardServiceImpl implements DashboardService {
@Autowired
private ShippingInvoiceMapper shippingInvoiceMapper;
@Autowired
private PurchaseOrderMapper purchaseOrderMapper;
@Autowired
private PlatformOrderMapper platformOrderMapper;
@Override
public Map<String,?> getKpis(LocalDateTime start, LocalDateTime end, List<String> roles, String username) {
boolean showAllData = roles.contains("admin") || roles.contains("dev");
LocalDateTime startLag = start.minusYears(1);
LocalDateTime endLag = end.minusYears(1);
InvoiceKpi shippingInvoices = shippingInvoiceMapper.countShippingInvoices(start, end, showAllData, username);
InvoiceKpi purchaseInvoices = purchaseOrderMapper.countPurchaseInvoices(start, end, showAllData, username);
OrderKpi platformOrders = platformOrderMapper.countPlatformOrders(start, end, showAllData, username);
InvoiceKpi shippingInvoicesLag = shippingInvoiceMapper.countShippingInvoices(startLag, endLag, showAllData, username);
InvoiceKpi purchaseInvoicesLag = purchaseOrderMapper.countPurchaseInvoices(startLag, endLag, showAllData, username);
OrderKpi platformOrdersLag = platformOrderMapper.countPlatformOrders(startLag, endLag, showAllData, username);
BigDecimal shipTotal = BigDecimal.valueOf(shippingInvoices.getTotal().doubleValue());
BigDecimal shipTotalLag = BigDecimal.valueOf(shippingInvoicesLag.getTotal().doubleValue());
BigDecimal purchaseTotal = BigDecimal.valueOf(purchaseInvoices.getTotal().doubleValue());
BigDecimal purchaseTotalLag = BigDecimal.valueOf(purchaseInvoicesLag.getTotal().doubleValue());
BigDecimal growthShippingInvoicesTotal = BigDecimal.ZERO;
BigDecimal growthShippingInvoicesQty = BigDecimal.ZERO;
BigDecimal growthPurchaseInvoicesTotal = BigDecimal.ZERO;
BigDecimal growthPurchaseInvoicesQty = BigDecimal.ZERO;
BigDecimal growthPlatformOrders = BigDecimal.ZERO;
if ((shipTotalLag.compareTo(BigDecimal.ZERO) > 0))
growthShippingInvoicesTotal = shipTotal.subtract(shipTotalLag).divide(shipTotalLag, 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
if ((shippingInvoicesLag.getQty() > 0))
growthShippingInvoicesQty = BigDecimal.valueOf(shippingInvoices.getQty()).subtract(BigDecimal.valueOf(shippingInvoicesLag.getQty())).divide(BigDecimal.valueOf(shippingInvoicesLag.getQty()), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
if (!(purchaseTotalLag.compareTo(BigDecimal.ZERO) > 0))
growthPurchaseInvoicesTotal = purchaseTotal.subtract(purchaseTotalLag).divide(purchaseTotalLag, 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
if ((purchaseInvoicesLag.getQty() > 0))
growthPurchaseInvoicesQty = BigDecimal.valueOf(purchaseInvoices.getQty()).subtract(BigDecimal.valueOf(purchaseInvoicesLag.getQty())).divide(BigDecimal.valueOf(purchaseInvoicesLag.getQty()), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
if ((platformOrdersLag.getProcessed() > 0))
growthPlatformOrders = BigDecimal.valueOf(platformOrders.getProcessed()).subtract(BigDecimal.valueOf(platformOrdersLag.getProcessed())).divide(BigDecimal.valueOf(platformOrdersLag.getProcessed()), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
shippingInvoices.setGrowthTotal(growthShippingInvoicesTotal);
shippingInvoices.setGrowthQty(growthShippingInvoicesQty);
purchaseInvoices.setGrowthTotal(growthPurchaseInvoicesTotal);
purchaseInvoices.setGrowthQty(growthPurchaseInvoicesQty);
platformOrders.setGrowth(growthPlatformOrders);
Map<String, Kpi> kpis = new HashMap<>();
kpis.put("shippingInvoices", shippingInvoices);
kpis.put("purchaseInvoices", purchaseInvoices);
kpis.put("platformOrders", platformOrders);
return kpis;
}
}

View File

@ -0,0 +1,233 @@
package org.jeecg.modules.business.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.entity.Balance;
import org.jeecg.modules.business.entity.Invoice;
import org.jeecg.modules.business.entity.PurchaseOrder;
import org.jeecg.modules.business.mapper.InvoiceMapper;
import org.jeecg.modules.business.mapper.PurchaseOrderContentMapper;
import org.jeecg.modules.business.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import static org.jeecg.modules.business.entity.Invoice.InvoiceType.*;
@Service
@Slf4j
public class InvoiceServiceImpl extends ServiceImpl<InvoiceMapper, Invoice> implements InvoiceService {
@Autowired
private IBalanceService balanceService;
@Autowired
private IClientService clientService;
@Autowired
private IPlatformOrderContentService platformOrderContentService;
@Autowired
private IPlatformOrderService platformOrderService;
@Autowired
private PurchaseOrderContentMapper purchaseOrderContentMapper;
@Autowired
private IPurchaseOrderService purchaseOrderService;
@Autowired
private ISavRefundService savRefundService;
@Autowired
private IShippingInvoiceService shippingInvoiceService;
@Value("${jeecg.path.purchaseInvoiceDir}")
private String PURCHASE_INVOICE_LOCATION;
@Value("${jeecg.path.shippingInvoiceDir}")
private String SHIPPING_INVOICE_LOCATION;
@Value("${jeecg.path.shippingInvoiceDetailDir}")
private String SHIPPING_INVOICE_DETAIL_LOCATION;
/**
* Cancel invoice and deletes generated files.
* shipping : deletes shipping_invoice, resets data in platform_order_content, platform_order, sav_refund, and balance
* purchase : deletes purchase_invoice, removes attachment files, removes number from platform_order
* complete : deletes purchase_invoice, removes attachment files, resets data in platform_order_content, platform_order, sav_refund, and balance
* @param id for shipping and complete invoices, it is the shipping_invoice id, for purchase invoices, it is the purchase_order id
* @param invoiceNumber invoice number to be cancelled
* @param clientId client id
* @return if invoice is successfully cancelled and files are deleted, will return false even when some files are just missing
*/
@Override
public boolean cancelInvoice(String id, String invoiceNumber, String clientId) {
String invoiceEntity = clientService.getById(clientId).getInvoiceEntity();
if(Invoice.getType(invoiceNumber).equalsIgnoreCase(PURCHASE.name())) {
PurchaseOrder po = purchaseOrderService.getById(id);
if (po.getInventoryDocumentString() != null && !po.getInventoryDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getInventoryDocumentString());
if (po.getPaymentDocumentString() != null && !po.getPaymentDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getPaymentDocumentString());
platformOrderService.removePurchaseInvoiceNumber(invoiceNumber);
purchaseOrderService.deleteInvoice(invoiceNumber);
}
if(Invoice.getType(invoiceNumber).equalsIgnoreCase(SHIPPING.name())) {
platformOrderContentService.cancelInvoice(invoiceNumber);
platformOrderService.cancelInvoice(invoiceNumber);
shippingInvoiceService.delMain(id);
}
if(Invoice.getType(invoiceNumber).equalsIgnoreCase(COMPLETE.name())) {
platformOrderContentService.cancelInvoice(invoiceNumber);
platformOrderService.removePurchaseInvoiceNumber(invoiceNumber);
platformOrderService.cancelInvoice(invoiceNumber);
purchaseOrderService.cancelInvoice(invoiceNumber);
shippingInvoiceService.delMain(id);
PurchaseOrder purchase = purchaseOrderService.getPurchaseByInvoiceNumberAndClientId(invoiceNumber, clientId);
if(purchase.getInventoryDocumentString() != null && !purchase.getInventoryDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(purchase.getInventoryDocumentString());
if(purchase.getPaymentDocumentString() != null && !purchase.getPaymentDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(purchase.getPaymentDocumentString());
}
savRefundService.cancelInvoice(invoiceNumber);
log.info("Updating balance ...");
balanceService.deleteBalance(id, Balance.OperationType.Debit.name());
log.info("Deleting invoice files ...");
boolean invoiceDeleted = deleteInvoice(invoiceNumber, invoiceEntity);
log.info("Invoice files deleted.");
return invoiceDeleted;
}
@Override
public boolean cancelBatchInvoice(List<Invoice> invoices) {
List<String> purchaseInvoiceNumbers = invoices.stream().map(Invoice::getInvoiceNumber).filter(invoiceNumber -> Invoice.getType(invoiceNumber).equalsIgnoreCase(PURCHASE.name())).collect(Collectors.toList());
List<String> shippingInvoiceNumbers = invoices.stream().map(Invoice::getInvoiceNumber).filter(invoiceNumber -> Invoice.getType(invoiceNumber).equalsIgnoreCase(SHIPPING.name())).collect(Collectors.toList());
List<String> completeInvoiceNumbers = invoices.stream().map(Invoice::getInvoiceNumber).filter(invoiceNumber -> Invoice.getType(invoiceNumber).equalsIgnoreCase(COMPLETE.name())).collect(Collectors.toList());
log.info("Cancelling {} purchase invoices : {}", purchaseInvoiceNumbers.size(), purchaseInvoiceNumbers);
log.info("Cancelling {} shipping invoices : {}", shippingInvoiceNumbers.size(), shippingInvoiceNumbers);
log.info("Cancelling {} complete invoices : {}", completeInvoiceNumbers.size(), completeInvoiceNumbers);
if(!purchaseInvoiceNumbers.isEmpty()) {
List<Invoice> purchaseInvoices = invoices.stream().filter(invoice -> Invoice.getType(invoice.getInvoiceNumber()).equalsIgnoreCase(PURCHASE.name())).collect(Collectors.toList());
List<String> ids = purchaseInvoices.stream().map(Invoice::getId).collect(Collectors.toList());
List<PurchaseOrder> purchaseOrders = purchaseOrderService.getPurchasesByInvoices(purchaseInvoices);
for(PurchaseOrder po : purchaseOrders) {
if(po.getInventoryDocumentString() != null && !po.getInventoryDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getInventoryDocumentString());
if(po.getPaymentDocumentString() != null && !po.getPaymentDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getPaymentDocumentString());
}
platformOrderService.removePurchaseInvoiceNumbers(purchaseInvoiceNumbers);
purchaseOrderService.cancelBatchInvoice(purchaseInvoiceNumbers);
purchaseOrderContentMapper.deleteFromPurchaseIds(ids);
savRefundService.cancelBatchInvoice(purchaseInvoiceNumbers);
log.info("Updating balances ...");
balanceService.deleteBatchBalance(ids, Balance.OperationType.Debit.name());
}
if(!shippingInvoiceNumbers.isEmpty()) {
List<String> ids = invoices.stream().filter(invoice -> Invoice.getType(invoice.getInvoiceNumber()).equalsIgnoreCase(SHIPPING.name())).map(Invoice::getId).collect(Collectors.toList());
platformOrderContentService.cancelBatchInvoice(shippingInvoiceNumbers);
platformOrderService.cancelBatchInvoice(shippingInvoiceNumbers);
savRefundService.cancelBatchInvoice(shippingInvoiceNumbers);
shippingInvoiceService.delBatchMain(ids);
log.info("Updating balances ...");
balanceService.deleteBatchBalance(ids, Balance.OperationType.Debit.name());
}
if(!completeInvoiceNumbers.isEmpty()) {
List<Invoice> completeInvoices = invoices.stream().filter(invoice -> Invoice.getType(invoice.getInvoiceNumber()).equalsIgnoreCase(COMPLETE.name())).collect(Collectors.toList());
List<String> ids = completeInvoices.stream().map(Invoice::getId).collect(Collectors.toList());
//shipping cancel
platformOrderContentService.cancelBatchInvoice(completeInvoiceNumbers);
platformOrderService.cancelBatchInvoice(completeInvoiceNumbers);
shippingInvoiceService.delBatchMain(ids);
//purchase cancel
List<PurchaseOrder> purchaseOrders = purchaseOrderService.getPurchasesByInvoices(completeInvoices);
for(PurchaseOrder po : purchaseOrders) {
if(po.getInventoryDocumentString() != null && !po.getInventoryDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getInventoryDocumentString());
if(po.getPaymentDocumentString() != null && !po.getPaymentDocumentString().isEmpty())
shippingInvoiceService.deleteAttachmentFile(po.getPaymentDocumentString());
}
platformOrderService.removePurchaseInvoiceNumbers(purchaseInvoiceNumbers);
purchaseOrderService.cancelBatchInvoice(purchaseInvoiceNumbers);
purchaseOrderContentMapper.deleteFromPurchaseIds(ids);
savRefundService.cancelBatchInvoice(completeInvoiceNumbers);
log.info("Updating balances ...");
balanceService.deleteBatchBalance(ids, Balance.OperationType.Debit.name());
}
// delete files
log.info("Deleting invoice files ...");
boolean invoiceDeleted = deleteInvoiceFiles(invoices);
log.info("Invoice files deleted.");
return invoiceDeleted;
}
/**
* Deletes invoice and detail files.
* @param invoiceNumber
* @param invoiceEntity
* @return
*/
public boolean deleteInvoice(String invoiceNumber, String invoiceEntity) {
boolean invoiceDeleted = false, detailDeleted = false;
List<Path> invoicePathList = shippingInvoiceService.getPath(Invoice.getType(invoiceNumber).equalsIgnoreCase(PURCHASE.name()) ? PURCHASE_INVOICE_LOCATION : SHIPPING_INVOICE_LOCATION, invoiceNumber, invoiceEntity);
List<Path> detailPathList = shippingInvoiceService.getPath(SHIPPING_INVOICE_DETAIL_LOCATION, invoiceNumber, invoiceEntity);
if(invoicePathList.isEmpty()) {
log.error("FILE NOT FOUND : " + invoiceNumber + ", " + invoiceEntity);
} else {
for (Path path : invoicePathList) {
log.info(path.toString());
}
try {
File invoiceFile = new File(invoicePathList.get(0).toString());
if(invoiceFile.delete()) {
log.info("Invoice file {} delete successful.", invoicePathList.get(0).toString());
invoiceDeleted = true;
} else {
log.error("Invoice file delete fail.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
if(detailPathList.isEmpty()) {
log.error("DETAIL FILE NOT FOUND : " + invoiceNumber + ", " + invoiceEntity);
} else {
for (Path path : detailPathList) {
log.info(path.toString());
}
try {
File detailFile = new File(detailPathList.get(0).toString());
if(detailFile.delete()) {
log.info("Detail file {} delete successful.", detailPathList.get(0).toString());
detailDeleted = true;
} else {
log.error("Detail file {} delete fail.", detailPathList.get(0).toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
return invoiceDeleted && detailDeleted;
}
public boolean deleteInvoiceFiles(List<Invoice> invoices) {
boolean invoicesDeleted = true;
for(Invoice invoice : invoices) {
String invoiceNumber = invoice.getInvoiceNumber();
String clientId = invoice.getClientId();
String invoiceEntity = clientService.getClientEntity(clientId);
log.info("Deleting invoice files ...");
invoicesDeleted &= deleteInvoice(invoiceNumber, invoiceEntity);
log.info("Invoice files deleted.");
}
return invoicesDeleted;
}
}

View File

@ -313,6 +313,15 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
Map<String, PlatformOrder> orderMap = orderList.stream().collect(toMap(PlatformOrder::getId, Function.identity()));
return orderContents.stream().collect(groupingBy(platformOrderContent -> orderMap.get(platformOrderContent.getPlatformOrderId())));
}
@Override
public Map<PlatformOrder, List<PlatformOrderContent>> fetchOrderDataByInvoiceCode(String invoiceCode) {
List<PlatformOrder> orderList = platformOrderMap.getPlatformOrdersByInvoiceNumber(invoiceCode);
List<String> orderIds = orderList.stream().map(PlatformOrder::getId).collect(toList());
List<PlatformOrderContent> orderContents = platformOrderContentMap.fetchOrderContent(orderIds);
Map<String, PlatformOrder> orderMap = orderList.stream().collect(toMap(PlatformOrder::getId, Function.identity()));
return orderContents.stream().collect(groupingBy(platformOrderContent -> orderMap.get(platformOrderContent.getPlatformOrderId())));
}
@Override
public String findPreviousInvoice() {
@ -477,4 +486,9 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
public List<PlatformOrder> getPlatformOrdersByInvoiceNumber(String invoiceNumber) {
return platformOrderMap.getPlatformOrdersByInvoiceNumber(invoiceNumber);
}
@Override
public Map<String, String> fetchShippingPeriodAndType(String invoiceNumber) {
return platformOrderMap.fetchShippingPeriodAndType(invoiceNumber);
}
}

View File

@ -24,6 +24,8 @@ import org.springframework.transaction.annotation.Transactional;
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;
@ -31,6 +33,7 @@ import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
@ -52,6 +55,8 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
private static final Integer DEFAULT_NUMBER_OF_THREADS = 1;
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 10;
private static final Integer SLEEP_TIME = 30000;
@Override
public void saveProviderFromMabang(List<ProviderData> providerDataList) {
if(providerDataList.isEmpty()) {
@ -74,7 +79,7 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
*/
@Transactional
@Override
public boolean addPurchaseOrderToMabang(Map<String, Integer> skuQuantities, InvoiceMetaData metaData) {
public boolean 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();
@ -145,10 +150,25 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<String> groupIds = new ArrayList<>();
List<String> groupIds = new ArrayList<>(); // results from Mabang API
List<CompletableFuture<Boolean>> changeOrderFutures = stockProviderMap.entrySet().stream()
.map(entry -> CompletableFuture.supplyAsync(() -> {
String providerName = entry.getKey();
if(providersHistory.get().containsKey(providerName)) {
log.info("Last processed provider : {} - {}", providerName, providersHistory.get().get(providerName));
LocalDateTime lastProcessed = providersHistory.get().get(providerName);
if(lastProcessed.isAfter(LocalDateTime.now().minusSeconds(10))) {
log.info("Thread sleeping for {} seconds to avoid rate limit on Mabang API", SLEEP_TIME/1000);
try {
long sleepTime = SLEEP_TIME - (LocalDateTime.now().toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli() - lastProcessed.toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());
System.out.println("======= SLEEP TIME : " + sleepTime);
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
log.error("Thread interrupted while sleeping", e);
}
}
}
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);
@ -160,6 +180,7 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
return false;
}
groupIds.add(response.getGroupId());
providersHistory.get().put(providerName, LocalDateTime.now());
return true;
}, throttlingExecutorService))
.collect(Collectors.toList());
@ -168,7 +189,10 @@ public class ProviderMabangServiceImpl extends ServiceImpl<ProviderMabangMapper,
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;
if(nbSuccesses == stockProviderMap.size()) {
purchaseOrderService.updatePurchaseOrderStatus(metaData.getInvoiceCode(), true);
return true;
}
return false;
}
}

View File

@ -28,8 +28,14 @@ public class SavRefundWithDetailServiceImpl extends ServiceImpl<SavRefundWithDet
return savRefundMapper.findUnprocessedRefundsByClient(clientId);
}
@Override
public List<SavRefundWithDetail> fetchRefundsWhere(String shop, String orderID, String column, String order) {
return savRefundMapper.fetchRefundsWhere(shop, orderID, column, order);
public List<SavRefundWithDetail> fetchRefundsWhere(String shop, String orderID, String invoiceNumber, String column, String order) {
String invoiceCode = invoiceNumber;
if(invoiceNumber.equals("%null%"))
invoiceCode = "null";
if(invoiceNumber.equals("%notNull%"))
invoiceCode = "notNull";
System.out.println("invoiceCode : "+invoiceCode);
return savRefundMapper.fetchRefundsWhere(shop, orderID, invoiceCode, column, order);
}
@Override

View File

@ -50,8 +50,10 @@ public class ShippingInvoiceServiceImpl extends ServiceImpl<ShippingInvoiceMappe
this.extension = extension;
}
}
@Value("${jeecg.path.purchaseInvoiceDir}")
private String PURCHASE_INVOICE_LOCATION;
@Value("${jeecg.path.shippingInvoiceDir}")
private String INVOICE_LOCATION;
private String SHIPPING_INVOICE_LOCATION;
@Value("${jeecg.path.shippingInvoiceDetailDir}")
private String INVOICE_DETAIL_LOCATION;
@Value("${jeecg.path.shippingInvoicePdfDir}")
@ -199,35 +201,6 @@ public class ShippingInvoiceServiceImpl extends ServiceImpl<ShippingInvoiceMappe
return pathList;
}
/**
* Finds the absolute path of invoice file and return the path
* @param invoiceNumber
* @param filetype if it's an invoice or invoice detail
* @return String returns the path of the invoice file
*/
public String getInvoiceList(String invoiceNumber, String filetype) {
log.info("Invoice number : " + invoiceNumber);
List<Path> pathList = new ArrayList<>();
if(filetype.equals("invoice")) {
log.info("File asked is of type invoice");
pathList = getPath(INVOICE_LOCATION, invoiceNumber);
}
if(filetype.equals("detail")) {
log.info("File asked is of type invoice detail");
pathList = getPath(INVOICE_DETAIL_LOCATION, invoiceNumber);
}
if(pathList.isEmpty()) {
log.error("NO INVOICE FILE FOUND : " + invoiceNumber);
return "ERROR";
}
else {
for (Path path : pathList) {
log.info(path.toString());
}
return pathList.get(0).toString();
}
}
@Override
public boolean deleteAttachmentFile(String filepath) {
String filename = filepath.substring(filepath.lastIndexOf("/")+1, filepath.lastIndexOf("."));
@ -257,4 +230,9 @@ public class ShippingInvoiceServiceImpl extends ServiceImpl<ShippingInvoiceMappe
}
return isFileDeleted;
}
@Override
public void setPaid(List<String> invoiceNumbers) {
shippingInvoiceMapper.setPaid(invoiceNumbers);
}
}

View File

@ -153,6 +153,11 @@ public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements ISkuS
}
}
@Override
public List<Sku> listSkus() {
return skuMapper.listSkus();
}
@Override
public List<Sku> selectByMainId(String mainId) {
return skuMapper.selectByMainId(mainId);
@ -425,4 +430,20 @@ public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements ISkuS
public String getIdFromErpCode(String erpCode) {
return skuMapper.getIdFromErpCode(erpCode);
}
@Override
public Sku getByErpCode(String erpCode) {
return skuMapper.getByErpCode(erpCode);
}
@Override
public void updateBatchStockByIds(List<Sku> skuToUpdate) {
skuMapper.updateBatchStockByIds(skuToUpdate);
}
@Override
public List<SkuOrderPage> getInventoryByInvoiceNumber(String invoiceNumber) {
return skuMapper.getInventoryByInvoiceNumber(invoiceNumber);
}
}

View File

@ -32,6 +32,7 @@ import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.stream.Collectors;
@ -44,21 +45,28 @@ import java.util.stream.Collectors;
@Slf4j
@Service
public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, PurchaseOrder> implements IPurchaseOrderService {
private final PurchaseOrderMapper purchaseOrderMapper;
private final PurchaseOrderContentMapper purchaseOrderContentMapper;
private final SkuPromotionHistoryMapper skuPromotionHistoryMapper;
private final ExchangeRatesMapper exchangeRatesMapper;
private final IClientService clientService;
private final IPlatformOrderService platformOrderService;
private final PlatformOrderMapper platformOrderMapper;
private final IPlatformOrderContentService platformOrderContentService;
private final IShippingInvoiceService shippingInvoiceService;
private final ISkuService skuService;
private final ICurrencyService currencyService;
@Autowired
private PurchaseOrderMapper purchaseOrderMapper;
@Autowired
private PurchaseOrderContentMapper purchaseOrderContentMapper;
@Autowired
private SkuPromotionHistoryMapper skuPromotionHistoryMapper;
@Autowired
private ExchangeRatesMapper exchangeRatesMapper;
@Autowired
private IClientService clientService;
@Autowired
private IPlatformOrderService platformOrderService;
@Autowired
private PlatformOrderMapper platformOrderMapper;
@Autowired
private IPlatformOrderContentService platformOrderContentService;
@Autowired
private IShippingInvoiceService shippingInvoiceService;
@Autowired
private ISkuService skuService;
@Autowired
private ICurrencyService currencyService;
/**
* Directory where payment documents are put
@ -81,25 +89,6 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
@Autowired
private PushMsgUtil pushMsgUtil;
public PurchaseOrderServiceImpl(PurchaseOrderMapper purchaseOrderMapper,
PurchaseOrderContentMapper purchaseOrderContentMapper,
SkuPromotionHistoryMapper skuPromotionHistoryMapper, IClientService clientService,
IPlatformOrderService platformOrderService, PlatformOrderMapper platformOrderMapper,
IPlatformOrderContentService platformOrderContentService, ISkuService skuService,
ExchangeRatesMapper exchangeRatesMapper, IShippingInvoiceService shippingInvoiceService, ICurrencyService currencyService) {
this.purchaseOrderMapper = purchaseOrderMapper;
this.purchaseOrderContentMapper = purchaseOrderContentMapper;
this.skuPromotionHistoryMapper = skuPromotionHistoryMapper;
this.clientService = clientService;
this.platformOrderService = platformOrderService;
this.platformOrderMapper = platformOrderMapper;
this.platformOrderContentService = platformOrderContentService;
this.skuService = skuService;
this.exchangeRatesMapper = exchangeRatesMapper;
this.shippingInvoiceService = shippingInvoiceService;
this.currencyService = currencyService;
}
@Override
@Transactional
public void saveMain(PurchaseOrder purchaseOrder, List<PurchaseOrderSku> purchaseOrderSkuList, List<SkuPromotionHistory> skuPromotionHistoryList) {
@ -181,114 +170,6 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
page.setTotal(total);
}
@Override
@Transactional
public void cancelInvoice(String purchaseId, String invoiceNumber) {
String invoiceEntity = clientService.getClientFromPurchase(purchaseId).getInvoiceEntity();
platformOrderService.removePurchaseInvoiceNumber(invoiceNumber);
purchaseOrderMapper.deleteInvoice(invoiceNumber);
List<Path> invoicePathList = shippingInvoiceService.getPath(INVOICE_DIR, invoiceNumber, invoiceEntity);
List<Path> detailPathList = shippingInvoiceService.getPath(INVOICE_DETAIL_DIR, invoiceNumber, invoiceEntity);
boolean invoiceDeleted = false, detailDeleted = false;
if(invoicePathList.isEmpty()) {
log.error("FILE NOT FOUND : " + invoiceNumber);
} else {
for (Path path : invoicePathList) {
log.info(path.toString());
}
try {
File invoiceFile = new File(invoicePathList.get(0).toString());
if(invoiceFile.delete()) {
log.info("Invoice file {} delete successful.", invoicePathList.get(0).toString());
invoiceDeleted = true;
} else {
log.error("Invoice file delete fail.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
if(detailPathList.isEmpty()) {
log.error("DETAIL FILE NOT FOUND : " + invoiceNumber);
} else {
for (Path path : detailPathList) {
log.info(path.toString());
}
try {
File detailFile = new File(detailPathList.get(0).toString());
if(detailFile.delete()) {
log.info("Detail file {} delete successful.", detailPathList.get(0).toString());
detailDeleted = true;
} else {
log.error("Detail file delete fail.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
log.info("Invoice cancel successful." + (invoiceDeleted ? "" : " Failed to delete invoice file.") + (detailDeleted ? "" : " Failed to delete detail file."));
}
@Transactional
@Override
public void cancelBatchInvoice(String purchaseIds) {
List<String> purchaseIdList = Arrays.asList(purchaseIds.split(","));
List<PurchaseOrder> purchaseOrders = purchaseOrderMapper.selectBatchIds(purchaseIdList);
List<String> invoiceNumbers = purchaseOrders.stream().map(PurchaseOrder::getInvoiceNumber).collect(Collectors.toList());
log.info("Cancelling invoices : {}", invoiceNumbers);
platformOrderService.removePurchaseInvoiceNumbers(invoiceNumbers);
cancelBatchInvoice(invoiceNumbers);
purchaseOrderContentMapper.deleteFromPurchaseIds(purchaseIdList);
log.info("Deleting invoice files ...");
for(PurchaseOrder purchaseOrder : purchaseOrders) {
if(purchaseOrder.getInvoiceNumber() == null)
continue;
String invoiceNumber = purchaseOrder.getInvoiceNumber();
String invoiceEntity = clientService.getClientEntity(purchaseOrder.getClientId());
List<Path> invoicePathList = shippingInvoiceService.getPath(INVOICE_DIR, invoiceNumber, invoiceEntity);
List<Path> detailPathList = shippingInvoiceService.getPath(INVOICE_DETAIL_DIR, invoiceNumber, invoiceEntity);
if(invoicePathList.isEmpty()) {
log.error("FILE NOT FOUND : " + invoiceNumber + ", " + invoiceEntity);
} else {
for (Path path : invoicePathList) {
log.info(path.toString());
}
try {
File invoiceFile = new File(invoicePathList.get(0).toString());
if(invoiceFile.delete()) {
log.info("Invoice file {} delete successful.", invoicePathList.get(0).toString());
} else {
log.error("Invoice file delete fail.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
if(detailPathList.isEmpty()) {
log.error("DETAIL FILE NOT FOUND : " + invoiceNumber + ", " + invoiceEntity);
} else {
for (Path path : detailPathList) {
log.info(path.toString());
}
try {
File detailFile = new File(detailPathList.get(0).toString());
if(detailFile.delete()) {
log.info("Detail file {} delete successful.", detailPathList.get(0).toString());
} else {
log.error("Detail file {} delete fail.", detailPathList.get(0).toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
System.gc();
}
log.info("End of invoice files deletion.");
}
@Transactional
@Override
public void confirmPayment(String purchaseID) {
@ -424,6 +305,7 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
}
/**
* Generated a purchase order based on sku quantity, hand-picked by sales
* sku.purchasingAmount is not updated, it is only updated via Mabang API
* @param skuQuantities a list of platform orders
* @return the purchase order's identifier (UUID)
*/
@ -480,7 +362,6 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
SkuQuantity::getQuantity
)
);
skuService.addSkuQuantity(quantityPurchased);
// 3. save the application of promotion information
List<PromotionHistoryEntry> promotionHistoryEntries = details.stream()
@ -655,13 +536,15 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
}
@Override
public InvoiceMetaData makeInvoice(String purchaseID) throws IOException, URISyntaxException {
public InvoiceMetaData makeInvoice(String purchaseID) throws IOException, UserException {
Client client = clientService.getCurrentClient();
if(client == null) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
if (sysUser.getOrgCode().contains("A01") || sysUser.getOrgCode().contains("A03")) {
client = clientService.getClientFromPurchase(purchaseID);
}
else
throw new UserException("User is not a client");
}
List<PurchaseInvoiceEntry> purchaseOrderSkuList = purchaseOrderContentMapper.selectInvoiceDataByID(purchaseID);
List<PromotionDetail> promotionDetails = skuPromotionHistoryMapper.selectPromotionByPurchase(purchaseID);
@ -671,13 +554,10 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
String filename = "Invoice N°" + invoiceCode + " (" + client.getInvoiceEntity() + ").xlsx";
Path template = Paths.get(INVOICE_TEMPLATE);
Path newInvoice = Paths.get(INVOICE_DIR, filename);
if (Files.notExists(newInvoice)) {
Files.copy(template, newInvoice);
PurchaseInvoice pv = new PurchaseInvoice(client, invoiceCode, "Purchase Invoice", purchaseOrderSkuList, promotionDetails, eurToUsd);
pv.toExcelFile(newInvoice);
return new InvoiceMetaData(filename,invoiceCode, pv.client().getInternalCode(), pv.client().getInvoiceEntity(), "");
}
return new InvoiceMetaData(filename, invoiceCode, client.getInternalCode(), client.getInvoiceEntity(), "");
Files.copy(template, newInvoice, StandardCopyOption.REPLACE_EXISTING);
PurchaseInvoice pv = new PurchaseInvoice(client, invoiceCode, "Purchase Invoice", purchaseOrderSkuList, promotionDetails, eurToUsd);
pv.toExcelFile(newInvoice);
return new InvoiceMetaData(filename,invoiceCode, pv.client().getInternalCode(), pv.client().getInvoiceEntity(), "");
}
@Override
@ -731,9 +611,9 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
}
@Override
public void setPageForList(Page<PurchaseOrderPage> page) {
public void setPageForList(Page<PurchaseOrderPage> page, String clientId) {
System.out.println("Offset: " + page.offset() + ", Size: " + page.getSize());
List<PurchaseOrderPage> purchaseOrderPages = purchaseOrderMapper.getPage(page.offset(), page.getSize());
List<PurchaseOrderPage> purchaseOrderPages = purchaseOrderMapper.getPage(page.offset(), page.getSize(), clientId);
page.setRecords(purchaseOrderPages);
page.setTotal(purchaseOrderMapper.countPurchaseOrders());
}
@ -742,4 +622,24 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
public void updatePurchaseOrderStatus(String invoiceNumber, boolean isOrdered) {
purchaseOrderMapper.updatePurchaseOrderStatus(invoiceNumber, isOrdered);
}
@Override
public void setPaid(List<String> invoiceNumber) {
purchaseOrderMapper.setPaid(invoiceNumber);
}
@Override
public void deleteInvoice(String invoiceNumber) {
purchaseOrderMapper.deleteInvoice(invoiceNumber);
}
@Override
public PurchaseOrder getPurchaseByInvoiceNumberAndClientId(String invoiceNumber, String clientId) {
return purchaseOrderMapper.getPurchaseByInvoiceNumberAndClientId(invoiceNumber, clientId);
}
@Override
public List<PurchaseOrder> getPurchasesByInvoices(List<Invoice> invoices) {
return purchaseOrderMapper.getPurchasesByInvoices(invoices);
}
}

View File

@ -0,0 +1,19 @@
package org.jeecg.modules.business.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class InvoiceKpi extends Kpi{
private Long qty;
private Long total;
private BigDecimal growthQty;
private BigDecimal growthTotal;
public InvoiceKpi(Long qty, Long total) {
this.qty = qty;
this.total = total;
}
}

View File

@ -0,0 +1,4 @@
package org.jeecg.modules.business.vo;
public class Kpi {
}

View File

@ -0,0 +1,18 @@
package org.jeecg.modules.business.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class OrderKpi extends Kpi {
private Long processed;
private Long processing;
private BigDecimal growth;
public OrderKpi(Long processed, Long processing) {
this.processed = processed;
this.processing = processing;
}
}

View File

@ -10,9 +10,13 @@ public class Period {
@JsonProperty
private final Date end;
public Period(Date start, Date end) {
@JsonProperty
private final String type;
public Period(Date start, Date end, String type) {
this.start = start;
this.end = end;
this.type = type;
}
public Date start() {

View File

@ -9,10 +9,10 @@ import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecgframework.poi.excel.annotation.ExcelCollection;
/**
* @Description: SKU
* @Description: SKU
* @Author: jeecg-boot
* @Date: 2021-08-13
* @Version: V1.2
* @Date: 2024-03-25
* @Version: V1.1
*/
@Data
@ApiModel(value = "skuOrderPage对象", description = "SKU表")
@ -26,10 +26,15 @@ public class SkuOrderPage {
/**
* ID
*/
@Excel(name = "商品ID", width = 15, dictTable = "product", dicText = "code", dicCode = "id")
@Dict(dictTable = "product", dicText = "code", dicCode = "id")
@ApiModelProperty(value = "商品ID")
@Excel(name = "商品", width = 15)
@ApiModelProperty(value = "商品")
private String product;
/**
* ID
*/
@Excel(name = "商品英文", width = 15)
@ApiModelProperty(value = "商品英文")
private String productEn;
/**
* ERP
*/
@ -48,6 +53,12 @@ public class SkuOrderPage {
@Excel(name = "在途数量", width = 15)
@ApiModelProperty(value = "在途数量")
private Integer purchasingAmount;
/**
*
*/
@Excel(name = "采购中数量", width = 15)
@ApiModelProperty(value = "采购中数量")
private Integer qtyOrdered;
/**
*
*/

View File

@ -250,7 +250,7 @@ public class SysPermissionController {
//添加首页路由
//update-begin-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
if(!PermissionDataUtil.hasIndexPage(metaList)){
SysPermission indexMenu = sysPermissionService.list(new LambdaQueryWrapper<SysPermission>().eq(SysPermission::getName,"首页")).get(0);
SysPermission indexMenu = sysPermissionService.list(new LambdaQueryWrapper<SysPermission>().eq(SysPermission::getName,"Home")).get(0);
metaList.add(0,indexMenu);
}
//update-end-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
@ -263,7 +263,7 @@ public class SysPermissionController {
//update-end--Author:zyf Date:20220425 for自定义首页地址 LOWCOD-1578
if(roleIndex!=null){
List<SysPermission> menus = metaList.stream().filter(sysPermission -> "首页".equals(sysPermission.getName())).collect(Collectors.toList());
List<SysPermission> menus = metaList.stream().filter(sysPermission -> "Home".equals(sysPermission.getName())).collect(Collectors.toList());
//update-begin---author:liusq ---date:2022-06-29 for设置自定义首页地址和组件----------
String component = roleIndex.getComponent();
String routeUrl = roleIndex.getUrl();

View File

@ -101,7 +101,7 @@ public class PermissionDataUtil {
public static boolean hasIndexPage(List<SysPermission> metaList){
boolean hasIndexMenu = false;
for (SysPermission sysPermission : metaList) {
if("首页".equals(sysPermission.getName())) {
if("Home".equals(sysPermission.getName())) {
hasIndexMenu = true;
break;
}

View File

@ -194,6 +194,7 @@ jeecg:
purchaseTemplatePath: /wia/files/Purchase_Invoice_Template.xlsx
# where to store generated file
purchaseInvoiceDir: /wia/invoices/purchase
purchaseInventoryDir: /wia/invoices/inventory
# shipping invoice template
shippingTemplatePath_EU: /wia/files/Shipping_Invoice_Template_EU.xlsx