mirror of https://github.com/jeecgboot/jeecg-boot
feature : confirmed clients invoicing job
parent
6978554075
commit
b718a23b9c
|
@ -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.domain.purchase.invoice.InvoiceData;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import org.jeecg.modules.business.service.*;
|
||||
|
@ -421,7 +422,7 @@ public class PurchaseOrderController {
|
|||
*/
|
||||
@AutoLog(value = "商品采购清单-通过客户id查询")
|
||||
@RequestMapping(value = "/admin/loadInventory")
|
||||
public Result<?> loadInventory(@RequestParam(name = "id") String clientId) {
|
||||
public Result<?> loadInventory(@RequestParam(name = "id") String clientId) throws UserException {
|
||||
Client client = clientService.getById(clientId);
|
||||
ClientInfo clientInfo = new ClientInfo(client);
|
||||
List<ImportedInventory> importedInventories = importedInventoryService.selectByClientId(clientId);
|
||||
|
|
|
@ -35,7 +35,8 @@ public class UserClientController {
|
|||
public Result<?> getClientByUserId() {
|
||||
LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
String userId = loginUser.getId();
|
||||
userId = "1708866308713140225";
|
||||
// userId = "1708866308713140225"; //EP
|
||||
// userId = "1721929497801580546"; //ND
|
||||
Client client = userClientService.getClientByUserId(userId);
|
||||
if(client == null) {
|
||||
List<SysRole> sysRoles = sysUserRoleService.getUserRole(userId);
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
|
||||
import javax.mail.Authenticator;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.PasswordAuthentication;
|
||||
import javax.mail.Session;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -235,6 +236,8 @@ public class InvoiceController {
|
|||
} catch (IOException | ParseException e) {
|
||||
log.error(e.getMessage());
|
||||
return Result.error("Sorry, server error, please try later");
|
||||
} catch (MessagingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,6 +283,8 @@ public class InvoiceController {
|
|||
} catch (IOException | ParseException e) {
|
||||
log.error(e.getMessage());
|
||||
return Result.error("Sorry, server error, please try later");
|
||||
} catch (MessagingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,7 +416,7 @@ public class InvoiceController {
|
|||
clientId = clientIDCodeMap.get(estimation.getCode());
|
||||
}
|
||||
else {
|
||||
clientId = clientService.getClientByInternalCode(estimation.getCode());
|
||||
clientId = clientService.getClientIdByCode(estimation.getCode());
|
||||
clientIDCodeMap.put(estimation.getCode(), clientId);
|
||||
}
|
||||
if (estimation.getIsCompleteInvoice().equals("1")) {
|
||||
|
@ -453,7 +458,7 @@ public class InvoiceController {
|
|||
});
|
||||
for(Map.Entry<String, List<ShippingFeesEstimation>> entry : estimationClientMap.entrySet()) {
|
||||
String code = entry.getKey();
|
||||
String clientId = clientService.getClientByInternalCode(code);
|
||||
String clientId = clientService.getClientIdByCode(code);
|
||||
List<String> shops = new ArrayList<>();
|
||||
int ordersToProcess = 0;
|
||||
int processedOrders = 0;
|
||||
|
|
|
@ -75,6 +75,8 @@ public class ShippingInvoiceController {
|
|||
@Autowired
|
||||
private IPlatformOrderService platformOrderService;
|
||||
@Autowired
|
||||
private IPurchaseOrderService purchaseOrderService;
|
||||
@Autowired
|
||||
private ISavRefundService savRefundService;
|
||||
@Autowired
|
||||
private IShippingInvoiceService shippingInvoiceService;
|
||||
|
@ -510,6 +512,7 @@ public class ShippingInvoiceController {
|
|||
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 ...");
|
||||
|
@ -571,6 +574,7 @@ public class ShippingInvoiceController {
|
|||
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);
|
||||
|
|
|
@ -12,6 +12,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.ClientPlatformOrderContent;
|
||||
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||
import org.jeecg.modules.business.entity.PlatformOrderContent;
|
||||
|
@ -192,7 +193,7 @@ public class ClientPlatformOrderController {
|
|||
notes = "Compute order statistic data of platform orders indicated by its identifier."
|
||||
)
|
||||
@PostMapping(value = "/computeInfo", consumes = "application/json", produces = "application/json")
|
||||
public Result<OrdersStatisticData> queryOrdersStatisticInfo(@RequestBody List<String> orderIds) {
|
||||
public Result<OrdersStatisticData> queryOrdersStatisticInfo(@RequestBody List<String> orderIds) throws UserException {
|
||||
log.info("Calculating statistic information for orders: {}", orderIds);
|
||||
OrdersStatisticData ordersData = platformOrderService.getPlatformOrdersStatisticData(orderIds);
|
||||
log.info("Got statistic information: {}", ordersData);
|
||||
|
@ -212,7 +213,7 @@ public class ClientPlatformOrderController {
|
|||
"client confirm information."
|
||||
)
|
||||
@PostMapping(value = "/placeOrder", consumes = "application/json", produces = "application/json")
|
||||
public Result<PurchaseConfirmation> placeOrder(@RequestBody List<String> orderIds) {
|
||||
public Result<PurchaseConfirmation> placeOrder(@RequestBody List<String> orderIds) throws UserException {
|
||||
log.info("One client place a purchase order");
|
||||
PurchaseConfirmation d = platformOrderService.confirmPurchaseByPlatformOrder(orderIds);
|
||||
log.info(d.toString());
|
||||
|
@ -226,7 +227,7 @@ public class ClientPlatformOrderController {
|
|||
* @return confirmation.
|
||||
*/
|
||||
@PostMapping(value = "/adjustOrder", consumes = "application/json", produces = "application/json")
|
||||
public Result<PurchaseConfirmation> adjustOrder(@RequestBody List<SkuQuantity> skuQuantities) {
|
||||
public Result<PurchaseConfirmation> adjustOrder(@RequestBody List<SkuQuantity> skuQuantities) throws UserException {
|
||||
log.info("One client adjust its purchase order");
|
||||
log.info("Content: {}", skuQuantities);
|
||||
PurchaseConfirmation d = platformOrderService.confirmPurchaseBySkuQuantity(skuQuantities);
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.swagger.annotations.ApiOperation;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.modules.business.controller.UserException;
|
||||
import org.jeecg.modules.business.controller.client.requestParams.PurchaseRequest;
|
||||
import org.jeecg.modules.business.entity.PurchaseOrder;
|
||||
import org.jeecg.modules.business.service.IPurchaseOrderService;
|
||||
|
@ -64,7 +65,7 @@ public class ClientPurchaseController {
|
|||
@AutoLog(value = "商品采购订单-添加")
|
||||
@ApiOperation(value = "商品采购订单-添加", notes = "商品采购订单-添加")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<String> addPurchaseOrder(@RequestBody PurchaseRequest purchaseRequest) {
|
||||
public Result<String> addPurchaseOrder(@RequestBody PurchaseRequest purchaseRequest) throws UserException {
|
||||
String id = purchaseOrderService.addPurchase(
|
||||
purchaseRequest.getSkuQuantity(),
|
||||
purchaseRequest.getPlatformOrderIDList()
|
||||
|
|
|
@ -3,7 +3,9 @@ package org.jeecg.modules.business.controller.client;
|
|||
import cn.hutool.core.date.DateTime;
|
||||
import io.swagger.annotations.Api;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.dto.message.TemplateMessageDTO;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.modules.business.domain.shippingInvoice.ShippingInvoiceFactory;
|
||||
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||
import org.jeecg.modules.business.entity.PlatformOrderContent;
|
||||
|
@ -13,6 +15,7 @@ import org.jeecg.modules.business.service.*;
|
|||
import org.jeecg.modules.business.vo.Estimation;
|
||||
import org.jeecg.modules.business.vo.ShippingFeesEstimation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
@ -28,6 +31,8 @@ import java.util.stream.Collectors;
|
|||
@RequestMapping("/transaction")
|
||||
@Slf4j
|
||||
public class TransactionController {
|
||||
@Autowired
|
||||
private EmailService emailService;
|
||||
@Autowired
|
||||
private TransactionMapper transactionMapper;
|
||||
@Autowired
|
||||
|
@ -68,6 +73,13 @@ public class TransactionController {
|
|||
ISavRefundWithDetailService savRefundWithDetailService;
|
||||
@Autowired
|
||||
ISavRefundService savRefundService;
|
||||
@Autowired
|
||||
private ISysBaseAPI ISysBaseApi;
|
||||
@Autowired
|
||||
Environment env;
|
||||
|
||||
private final String SECTION_START = "<section><ul>";
|
||||
private final String SECTION_END = "</ul></section>";
|
||||
@GetMapping(value="/list")
|
||||
public Result<?> list() {
|
||||
return Result.ok(transactionMapper.list());
|
||||
|
@ -91,7 +103,7 @@ public class TransactionController {
|
|||
ShippingInvoiceFactory factory = new ShippingInvoiceFactory(
|
||||
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
|
||||
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService);
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService, emailService, env);
|
||||
List<ShippingFeesEstimation> shippingFeesEstimations = factory.getEstimations(clientId, orderIds, errorMessages);
|
||||
if(shippingFeesEstimations.isEmpty())
|
||||
return Result.OK("No estimation found.");
|
||||
|
@ -131,6 +143,28 @@ public class TransactionController {
|
|||
System.out.println("Purchase Fee " + currency + " : " + purchaseEstimation);
|
||||
System.out.println("Shipping Fee " + currency + " : " + shippingFeesEstimation);
|
||||
}
|
||||
// system notification
|
||||
String errors = SECTION_START;
|
||||
int max_entries = 100;
|
||||
int current_page = 0;
|
||||
int total_page = (int) Math.ceil((double) errorMessages.size() /max_entries);
|
||||
for(int i = 1; i <= errorMessages.size(); i++) {
|
||||
if(i%max_entries == 1) {
|
||||
errors = SECTION_START;
|
||||
current_page++;
|
||||
}
|
||||
errors = errors.concat("<li>" + i + " : " + errorMessages.get(i-1) +"</li>");
|
||||
if(i%max_entries==0 || i == errorMessages.size()) {
|
||||
errors = errors.concat(SECTION_END);
|
||||
Map<String, String> param = new HashMap<>();
|
||||
param.put("nb_entries", String.valueOf(errorMessages.size()));
|
||||
param.put("errors", errors);
|
||||
param.put("current_page", String.valueOf(current_page));
|
||||
param.put("total_page", String.valueOf(total_page));
|
||||
TemplateMessageDTO message = new TemplateMessageDTO("admin", "Gauthier", "Expenses Overview Errors", param, "expenses_overview_errors");
|
||||
ISysBaseApi.sendTemplateAnnouncement(message);
|
||||
}
|
||||
}
|
||||
return Result.ok(new Estimation(shippingFeesEstimation, purchaseEstimation, currency, errorMessages, shopIds, new DateTime(startDate).toString(), new DateTime(endDate).toString(), isCompleteInvoiceReady));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
package org.jeecg.modules.business.domain.job;
|
||||
|
||||
import freemarker.template.Template;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.modules.business.entity.Client;
|
||||
import org.jeecg.modules.business.entity.SavRefundWithDetail;
|
||||
import org.jeecg.modules.business.service.*;
|
||||
import org.jeecg.modules.business.vo.BalanceData;
|
||||
import org.jeecg.modules.business.vo.FactureDetail;
|
||||
import org.jeecg.modules.business.vo.InvoiceMetaData;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
|
||||
import javax.mail.Authenticator;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.PasswordAuthentication;
|
||||
import javax.mail.Session;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class ConfirmedClientsInvoicingJob implements Job {
|
||||
@Autowired
|
||||
private IBalanceService balanceService;
|
||||
@Autowired
|
||||
private IClientService clientService;
|
||||
@Autowired
|
||||
private EmailService emailService;
|
||||
@Autowired
|
||||
private PlatformOrderShippingInvoiceService platformOrderShippingInvoiceService;
|
||||
@Autowired
|
||||
private ISavRefundWithDetailService savRefundWithDetailService;
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
@Autowired
|
||||
private FreeMarkerConfigurer freemarkerConfigurer;
|
||||
@Override
|
||||
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||
log.info("Confirmed clients Invoicing Job started ...");
|
||||
List<Client> confirmedClients = clientService.getClientsByType("confirmed");
|
||||
List<Client> clients = new ArrayList<>();
|
||||
List<BalanceData> balanceDataList = new ArrayList<>();
|
||||
List<BalanceData> shippingBalanceDataList = new ArrayList<>();
|
||||
List<BalanceData> completeBalanceDataList = new ArrayList<>();
|
||||
for(Client client : confirmedClients) {
|
||||
BigDecimal balance = balanceService.getBalanceByClientIdAndCurrency(client.getId(), client.getCurrency());
|
||||
if(balance.compareTo(BigDecimal.ZERO) > 0) {
|
||||
clients.add(client);
|
||||
balanceDataList.add(new BalanceData(client, client.getCurrency(), balance));
|
||||
if(client.getIsCompleteInvoice().equals("0")) {
|
||||
shippingBalanceDataList.add(new BalanceData(client, client.getCurrency(), balance));
|
||||
}
|
||||
else {
|
||||
completeBalanceDataList.add(new BalanceData(client, client.getCurrency(), balance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("shipping clients list size : " + shippingBalanceDataList.size());
|
||||
log.info("complete clients list size : " + completeBalanceDataList.size());
|
||||
List<InvoiceMetaData> invoiceList = new ArrayList<>();
|
||||
// we need to make sure that the client has enough balance to be invoiced
|
||||
// step 1 : get the list of clients that have enough positive balance
|
||||
// step 2 : calculer au pro rata le montant de la facture pour qu'elle ne dépasse pas le solde du client
|
||||
// step 3 : update balance
|
||||
// step 4 : generate invoice
|
||||
// step 5 : send mail to client if balance is low
|
||||
if(!shippingBalanceDataList.isEmpty()) {
|
||||
log.info("Making shipping invoice for clients : {}", shippingBalanceDataList);
|
||||
invoiceList = new ArrayList<>(platformOrderShippingInvoiceService.breakdownInvoiceClientByTypeAndBalance(shippingBalanceDataList, 0));
|
||||
}
|
||||
if(!completeBalanceDataList.isEmpty()) {
|
||||
log.info("Making complete shipping invoice for clients : {}", completeBalanceDataList);
|
||||
invoiceList.addAll(platformOrderShippingInvoiceService.breakdownInvoiceClientByTypeAndBalance(completeBalanceDataList, 1));
|
||||
}
|
||||
if(invoiceList.isEmpty()) {
|
||||
log.info("Nothing to invoice.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<InvoiceMetaData> metaDataErrorList = new ArrayList<>();
|
||||
List<InvoiceMetaData> invoicedMetaDataList = new ArrayList<>(); // list of invoice metadata that has been invoiced
|
||||
log.info("Generating detail files ...0/{}", invoiceList.size());
|
||||
int cpt = 1;
|
||||
for(InvoiceMetaData metaData: invoiceList){
|
||||
if(metaData.getInvoiceCode().equals("error")) {
|
||||
metaDataErrorList.add(metaData);
|
||||
}
|
||||
else {
|
||||
invoicedMetaDataList.add(metaData);
|
||||
List<FactureDetail> factureDetails = platformOrderShippingInvoiceService.getInvoiceDetail(metaData.getInvoiceCode());
|
||||
List<SavRefundWithDetail> refunds = savRefundWithDetailService.getRefundsByInvoiceNumber(metaData.getInvoiceCode());
|
||||
try {
|
||||
platformOrderShippingInvoiceService.exportToExcel(factureDetails, refunds, metaData.getInvoiceCode(), metaData.getInvoiceEntity(), metaData.getInternalCode());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
log.info("Generating detail files ...{}/{}", cpt++, invoiceList.size());
|
||||
}
|
||||
if(!metaDataErrorList.isEmpty()) {
|
||||
String subject = "[" + LocalDate.now() + "] Confirmed clients invoicing job report";
|
||||
String destEmail = env.getProperty("spring.mail.username");
|
||||
String templateName = "vipInvoicingJobReport.ftl";
|
||||
Map<String, Object> templateModel = new HashMap<>();
|
||||
templateModel.put("job", "Confirmed clients");
|
||||
templateModel.put("errors", metaDataErrorList);
|
||||
try {
|
||||
emailService.newSendSimpleMessage(destEmail, subject, templateName, templateModel);
|
||||
} catch (MessagingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// emailing for low balance clients
|
||||
List<BalanceData> lowBalanceDataList = balanceService.getLowBalanceClients(invoicedMetaDataList);
|
||||
//todo : send mail to clients in prod
|
||||
if(!lowBalanceDataList.isEmpty()) {
|
||||
for(BalanceData data : lowBalanceDataList) {
|
||||
log.info("Low balance client : {}", data.getClient().getInternalCode());
|
||||
String subject = "[" + LocalDate.now() + "] WIA App low balance notification";
|
||||
String destEmail = env.getProperty("spring.mail.username");
|
||||
Properties prop = emailService.getMailSender();
|
||||
Map<String, Object> templateModel = new HashMap<>();
|
||||
templateModel.put("firstname", data.getClient().getFirstName());
|
||||
templateModel.put("lastname", data.getClient().getSurname());
|
||||
templateModel.put("balance", data.getBalance());
|
||||
templateModel.put("currency", data.getCurrency());
|
||||
templateModel.put("clientCategory", "confirmed");
|
||||
|
||||
Session session = Session.getInstance(prop, new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(env.getProperty("spring.mail.username"), env.getProperty("spring.mail.password"));
|
||||
}
|
||||
});
|
||||
try {
|
||||
freemarkerConfigurer = emailService.freemarkerClassLoaderConfig();
|
||||
Template freemarkerTemplate = freemarkerConfigurer.getConfiguration()
|
||||
.getTemplate("client/lowBalanceNotification.ftl");
|
||||
String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);
|
||||
emailService.sendSimpleMessage(destEmail, subject, htmlBody, session);
|
||||
//todo : update balance_notification date and reason
|
||||
log.info("Mail sent successfully !");
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending low balance notification mail in VipInvoicingJob : ", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("Confirmed clients invoicing job finished.");
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@ package org.jeecg.modules.business.domain.job;
|
|||
|
||||
import freemarker.template.Template;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import org.jeecg.modules.business.service.*;
|
||||
import org.jeecg.modules.business.vo.BalanceData;
|
||||
import org.jeecg.modules.business.vo.FactureDetail;
|
||||
import org.jeecg.modules.business.vo.InvoiceMetaData;
|
||||
import org.quartz.Job;
|
||||
|
@ -19,12 +19,15 @@ import javax.mail.Authenticator;
|
|||
import javax.mail.PasswordAuthentication;
|
||||
import javax.mail.Session;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class VipInvoicingJob implements Job {
|
||||
@Autowired
|
||||
private IBalanceService balanceService;
|
||||
@Autowired
|
||||
private IClientService clientService;
|
||||
@Autowired
|
||||
|
@ -40,7 +43,7 @@ public class VipInvoicingJob implements Job {
|
|||
private FreeMarkerConfigurer freemarkerConfigurer;
|
||||
@Override
|
||||
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||
log.info("VIP Invoicing Job executed.");
|
||||
log.info("VIP Invoicing Job started ...");
|
||||
List<Client> clients = clientService.getClientsByType("vip");
|
||||
List<String> shippingClientIds = clients.stream().filter(client -> client.getIsCompleteInvoice().equals("0")).map(Client::getId).collect(Collectors.toList());
|
||||
List<String> completeClientIds = clients.stream().filter(client -> client.getIsCompleteInvoice().equals("1")).map(Client::getId).collect(Collectors.toList());
|
||||
|
@ -62,6 +65,7 @@ public class VipInvoicingJob implements Job {
|
|||
}
|
||||
|
||||
List<InvoiceMetaData> metaDataErrorList = new ArrayList<>();
|
||||
List<InvoiceMetaData> invoicedMetaDataList = new ArrayList<>(); // list of invoice meta data that has been invoiced
|
||||
log.info("Generating detail files ...0/{}", invoiceList.size());
|
||||
int cpt = 1;
|
||||
for(InvoiceMetaData metaData: invoiceList){
|
||||
|
@ -69,6 +73,7 @@ public class VipInvoicingJob implements Job {
|
|||
metaDataErrorList.add(metaData);
|
||||
}
|
||||
else {
|
||||
invoicedMetaDataList.add(metaData);
|
||||
List<FactureDetail> factureDetails = platformOrderShippingInvoiceService.getInvoiceDetail(metaData.getInvoiceCode());
|
||||
List<SavRefundWithDetail> refunds = savRefundWithDetailService.getRefundsByInvoiceNumber(metaData.getInvoiceCode());
|
||||
try {
|
||||
|
@ -84,6 +89,7 @@ public class VipInvoicingJob implements Job {
|
|||
String destEmail = env.getProperty("spring.mail.username");
|
||||
Properties prop = emailService.getMailSender();
|
||||
Map<String, Object> templateModel = new HashMap<>();
|
||||
templateModel.put("job", "VIP");
|
||||
templateModel.put("errors", metaDataErrorList);
|
||||
|
||||
Session session = Session.getInstance(prop, new Authenticator() {
|
||||
|
@ -104,6 +110,43 @@ public class VipInvoicingJob implements Job {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// emailing for low balance clients
|
||||
List<BalanceData> lowBalanceDataList = balanceService.getLowBalanceClients(invoicedMetaDataList);
|
||||
|
||||
if(!lowBalanceDataList.isEmpty()) {
|
||||
for(BalanceData data : lowBalanceDataList) {
|
||||
log.info("Low balance client : {}", data.getClient().getInternalCode());
|
||||
String subject = "[" + LocalDate.now() + "] WIA App low balance notification";
|
||||
// TODO : change destEmail to client email for prod
|
||||
String destEmail = env.getProperty("spring.mail.username");
|
||||
Properties prop = emailService.getMailSender();
|
||||
Map<String, Object> templateModel = new HashMap<>();
|
||||
templateModel.put("firstname", data.getClient().getFirstName());
|
||||
templateModel.put("lastname", data.getClient().getSurname());
|
||||
templateModel.put("balance", data.getBalance());
|
||||
templateModel.put("currency", data.getCurrency());
|
||||
templateModel.put("clientCategory", "vip");
|
||||
|
||||
Session session = Session.getInstance(prop, new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(env.getProperty("spring.mail.username"), env.getProperty("spring.mail.password"));
|
||||
}
|
||||
});
|
||||
try {
|
||||
freemarkerConfigurer = emailService.freemarkerClassLoaderConfig();
|
||||
Template freemarkerTemplate = freemarkerConfigurer.getConfiguration()
|
||||
.getTemplate("client/lowBalanceNotification.ftl");
|
||||
String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);
|
||||
emailService.sendSimpleMessage(destEmail, subject, htmlBody, session);
|
||||
//todo : update balance_notification date and reason
|
||||
log.info("Mail sent successfully !");
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending low balance notification mail in VipInvoicingJob : ", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("VIP invoicing job finished.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.google.common.cache.CacheBuilder;
|
|||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.catalina.User;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jeecg.modules.business.controller.UserException;
|
||||
import org.jeecg.modules.business.domain.codeGeneration.CompleteInvoiceCodeRule;
|
||||
|
@ -16,15 +17,20 @@ import org.jeecg.modules.business.vo.PromotionDetail;
|
|||
import org.jeecg.modules.business.vo.ShippingFeesEstimation;
|
||||
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.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
@ -51,6 +57,8 @@ public class ShippingInvoiceFactory {
|
|||
private final SkuPromotionHistoryMapper skuPromotionHistoryMapper;
|
||||
private final ISavRefundService savRefundService;
|
||||
private final ISavRefundWithDetailService savRefundWithDetailService;
|
||||
private final EmailService emailService;
|
||||
private final Environment env;
|
||||
|
||||
private final SimpleDateFormat SUBJECT_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
|
@ -78,7 +86,7 @@ public class ShippingInvoiceFactory {
|
|||
ExchangeRatesMapper exchangeRatesMapper, IPurchaseOrderService purchaseOrderService,
|
||||
PurchaseOrderContentMapper purchaseOrderContentMapper,
|
||||
SkuPromotionHistoryMapper skuPromotionHistoryMapper, ISavRefundService savRefundService,
|
||||
ISavRefundWithDetailService savRefundWithDetailService) {
|
||||
ISavRefundWithDetailService savRefundWithDetailService, EmailService emailService, Environment env) {
|
||||
this.platformOrderService = platformOrderService;
|
||||
this.clientMapper = clientMapper;
|
||||
this.shopMapper = shopMapper;
|
||||
|
@ -93,6 +101,8 @@ public class ShippingInvoiceFactory {
|
|||
this.skuPromotionHistoryMapper = skuPromotionHistoryMapper;
|
||||
this.savRefundService = savRefundService;
|
||||
this.savRefundWithDetailService = savRefundWithDetailService;
|
||||
this.emailService = emailService;
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +170,7 @@ public class ShippingInvoiceFactory {
|
|||
* channel price, this exception will be thrown.
|
||||
*/
|
||||
@Transactional
|
||||
public CompleteInvoice createCompleteShippingInvoice(String username, String customerId, List<String> orderIds, String shippingMethod, String start, String end) throws UserException {
|
||||
public CompleteInvoice createCompleteShippingInvoice(String username, String customerId, BigDecimal balance, List<String> orderIds, String shippingMethod, String start, String end) throws UserException, MessagingException {
|
||||
log.info("Creating a complete invoice for \n client ID: {}, order IDs: {}]", customerId, orderIds);
|
||||
// find orders and their contents of the invoice
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> uninvoicedOrderToContent = platformOrderService.fetchOrderData(orderIds);
|
||||
|
@ -181,8 +191,9 @@ public class ShippingInvoiceFactory {
|
|||
else if(shippingMethod.equals("all"))
|
||||
subject = String.format("Purchase and Shipping fees, order time from %s to %s", start, end);
|
||||
else throw new UserException("Couldn't create complete invoice for unknown shipping method");
|
||||
|
||||
return createInvoice(username, customerId, shopIds, uninvoicedOrderToContent, savRefunds, subject);
|
||||
if(balance != null)
|
||||
return createCompleteInvoiceWithBalance(username, customerId, balance, shopIds, uninvoicedOrderToContent, savRefunds, subject);
|
||||
return createInvoice(username, customerId, null, shopIds, uninvoicedOrderToContent, savRefunds, subject);
|
||||
}
|
||||
|
||||
|
||||
|
@ -209,7 +220,7 @@ public class ShippingInvoiceFactory {
|
|||
* channel price, this exception will be thrown.
|
||||
*/
|
||||
@Transactional
|
||||
public CompleteInvoice createInvoice(String username, String customerId, List<String> shopIds,
|
||||
public CompleteInvoice createInvoice(String username, String customerId, BigDecimal balance, List<String> shopIds,
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent,
|
||||
List<SavRefundWithDetail> savRefunds, String subject) throws UserException {
|
||||
Client client = clientMapper.selectById(customerId);
|
||||
|
@ -235,10 +246,145 @@ public class ShippingInvoiceFactory {
|
|||
shops.forEach(shop -> shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee()));
|
||||
String invoiceCode = generateCompleteInvoiceCode();
|
||||
log.info("New invoice code: {}", invoiceCode);
|
||||
calculateFees(logisticChannelMap, orderAndContent, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
calculateFees(balance, logisticChannelMap, orderAndContent, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
latestDeclaredValues, client, shopServiceFeeMap, shopPackageMatFeeMap, invoiceCode);
|
||||
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
|
||||
List<String> orderIds = orderAndContent.keySet().stream().map(PlatformOrder::getId).collect(toList());
|
||||
List<SkuQuantity> skuQuantities = platformOrderContentService.searchOrderContent(orderIds);
|
||||
|
||||
String purchaseID = purchaseOrderService.addPurchase(username, client, invoiceCode, skuQuantities, orderAndContent);
|
||||
|
||||
List<PurchaseInvoiceEntry> purchaseOrderSkuList = purchaseOrderContentMapper.selectInvoiceDataByID(purchaseID);
|
||||
List<PromotionDetail> promotionDetails = skuPromotionHistoryMapper.selectPromotionByPurchase(purchaseID);
|
||||
if (savRefunds != null) {
|
||||
updateSavRefundsInDb(savRefunds, invoiceCode);
|
||||
}
|
||||
|
||||
updateOrdersAndContentsInDb(orderAndContent);
|
||||
|
||||
return new CompleteInvoice(client, invoiceCode, subject, orderAndContent, savRefunds,
|
||||
purchaseOrderSkuList, promotionDetails, eurToUsd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a complete invoice for a client, based of its balance, a list of shops, a date range.
|
||||
* <p>
|
||||
* To generate an invoice, it
|
||||
* <ol>
|
||||
* <li>Search orders and their contents based on shop and date range</li>
|
||||
* <li>Generate a new invoice code</li>
|
||||
* <li>Find proper logistic channel price for each order </li>
|
||||
* <li>Update prices of orders and their contents</li>
|
||||
* <li>Generate a invoice</li>
|
||||
* <li>Update invoiced their orders and contents to DB</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param username Current username
|
||||
* @param customerId Customer ID
|
||||
* @param balance Customer balance
|
||||
* @param shopIds Shop IDs
|
||||
* @param savRefunds List of SAV refunds
|
||||
* @param subject Invoice subject
|
||||
* @return the generated invoice
|
||||
* @throws UserException if package used by the invoice can not or find more than 1 logistic
|
||||
* channel price, this exception will be thrown.
|
||||
*/
|
||||
@Transactional
|
||||
public CompleteInvoice createCompleteInvoiceWithBalance(String username, String customerId, BigDecimal balance, List<String> shopIds,
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent,
|
||||
List<SavRefundWithDetail> savRefunds, String subject) throws UserException, MessagingException {
|
||||
// sorting by order time
|
||||
orderAndContent = orderAndContent.entrySet().stream().sorted(
|
||||
Map.Entry.comparingByKey(Comparator.comparing(PlatformOrder::getOrderTime))
|
||||
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
|
||||
|
||||
Client client = clientMapper.selectById(customerId);
|
||||
BigDecimal virtualBalance = balance;
|
||||
Map<String, List<String>> ordersToRemove = new HashMap<>();
|
||||
log.info("User {} is creating a complete invoice in {} order, for customer {}", username, client.getInternalCode(), client.getIsChronologicalOrder().equals("0") ? "first can invoice" : "chronological");
|
||||
|
||||
if (orderAndContent == null || orderAndContent.isEmpty()) {
|
||||
throw new UserException("No platform order in the selected period!");
|
||||
}
|
||||
log.info("Orders to be invoiced: {}", orderAndContent);
|
||||
Map<String, BigDecimal> skuRealWeights = new HashMap<>();
|
||||
Map<String, BigDecimal> skuServiceFees = new HashMap<>();
|
||||
skuDataPreparation(skuRealWeights, skuServiceFees);
|
||||
List<Country> countryList = countryService.findAll();
|
||||
Map<String, LogisticChannel> logisticChannelMap = logisticChannelMapper.getAll().stream()
|
||||
.collect(toMap(LogisticChannel::getId, Function.identity()));
|
||||
Map<LogisticChannel, List<LogisticChannelPrice>> channelPriceMap = getChannelPriceMap(logisticChannelMap, orderAndContent, true);
|
||||
List<SkuDeclaredValue> latestDeclaredValues = skuDeclaredValueService.getLatestDeclaredValues();
|
||||
|
||||
List<Shop> shops = shopMapper.selectBatchIds(shopIds);
|
||||
Map<String, BigDecimal> shopServiceFeeMap = new HashMap<>();
|
||||
Map<String, BigDecimal> shopPackageMatFeeMap = new HashMap<>();
|
||||
shops.forEach(shop -> shopServiceFeeMap.put(shop.getId(), shop.getOrderServiceFee()));
|
||||
shops.forEach(shop -> shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee()));
|
||||
String invoiceCode = generateCompleteInvoiceCode();
|
||||
|
||||
log.info("New invoice code: {}", invoiceCode);
|
||||
|
||||
boolean skip = false;// isChronologicalOrder = 1 && insufficient balance => skip = true
|
||||
for(Map.Entry<PlatformOrder, List<PlatformOrderContent>> entry : orderAndContent.entrySet()) {
|
||||
if(skip) {
|
||||
if(ordersToRemove.containsKey("skip"))
|
||||
ordersToRemove.get("skip").add(entry.getKey().getPlatformOrderId());
|
||||
else
|
||||
ordersToRemove.put("skip", Collections.singletonList(entry.getKey().getPlatformOrderId()));
|
||||
continue;
|
||||
}
|
||||
BigDecimal estimatedVirtualBalance;
|
||||
try {
|
||||
estimatedVirtualBalance = calculateFeeForOrder(username, virtualBalance, logisticChannelMap, entry.getKey(), entry.getValue(), channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
latestDeclaredValues, client, shopServiceFeeMap, shopPackageMatFeeMap, invoiceCode);
|
||||
} catch (UserException e) {
|
||||
log.error("Couldn't calculate fee for order {} !", entry.getKey().getId());
|
||||
if(ordersToRemove.containsKey("error"))
|
||||
ordersToRemove.get("error").add(entry.getKey().getPlatformOrderId());
|
||||
else
|
||||
ordersToRemove.put("error", Collections.singletonList(entry.getKey().getPlatformOrderId()));
|
||||
continue;
|
||||
}
|
||||
if(estimatedVirtualBalance.compareTo(BigDecimal.ZERO) < 0) {
|
||||
log.error("Not enough balance for order {} !", entry.getKey().getId());
|
||||
if(ordersToRemove.containsKey("balance"))
|
||||
ordersToRemove.get("balance").add(entry.getKey().getPlatformOrderId());
|
||||
else
|
||||
ordersToRemove.put("balance", Collections.singletonList(entry.getKey().getPlatformOrderId()));
|
||||
if(client.getIsChronologicalOrder().equals("1"))
|
||||
skip = true;
|
||||
continue;
|
||||
}
|
||||
virtualBalance = estimatedVirtualBalance;
|
||||
}
|
||||
if(!ordersToRemove.isEmpty() && ordersToRemove.get("balance") != null) {
|
||||
String emailSubject = "[" + LocalDate.now() + "] Rapport de facturation automatique WIA App";
|
||||
String destEmail = env.getProperty("spring.mail.username");
|
||||
String templateName = "client/confirmedClientsInvoicingJobReport.ftl";
|
||||
Map<String, Object> templateModel = new HashMap<>();
|
||||
templateModel.put("errors", ordersToRemove.get("balance"));
|
||||
// templateModel.put("skipped", ordersToRemove.get("skip"));
|
||||
templateModel.put("client", client);
|
||||
templateModel.put("chronologicalOrder", client.getIsChronologicalOrder());
|
||||
emailService.newSendSimpleMessage(destEmail, emailSubject, templateName, templateModel);
|
||||
}
|
||||
// removing orders that can't be invoiced
|
||||
System.out.println("Orders and content size BEFORE : " + orderAndContent.size());
|
||||
System.out.println("Orders to remove size : " + ordersToRemove.size());
|
||||
for(Map.Entry<String, List<String>> entry : ordersToRemove.entrySet()) {
|
||||
for(String platformOrderId : entry.getValue()) {
|
||||
orderAndContent.keySet().removeIf(order -> order.getPlatformOrderId().equals(platformOrderId));
|
||||
}
|
||||
}
|
||||
System.out.println("Orders and content size AFTER : " + orderAndContent.size());
|
||||
|
||||
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
|
||||
if(orderAndContent.isEmpty()) {
|
||||
log.error("No order was invoiced for customer {}, : Please check if the orders are in the correct erp status and if the customer has enough balance.", client.getInternalCode());
|
||||
throw new UserException("No order was invoiced for customer " + client.getInternalCode() +
|
||||
" : Please check if the orders are in the correct erp status and if the customer has enough balance.");
|
||||
}
|
||||
List<String> orderIds = orderAndContent.keySet().stream().map(PlatformOrder::getId).collect(toList());
|
||||
List<SkuQuantity> skuQuantities = platformOrderContentService.searchOrderContent(orderIds);
|
||||
|
||||
|
@ -328,7 +474,7 @@ public class ShippingInvoiceFactory {
|
|||
* channel price, this exception will be thrown.
|
||||
*/
|
||||
@Transactional
|
||||
public ShippingInvoice createInvoice(String customerId, List<String> shopIds, Date begin, Date end, List<Integer> erpStatuses, List<String> warehouses) throws UserException {
|
||||
public ShippingInvoice createInvoice(String customerId, List<String> shopIds, Date begin, Date end, List<Integer> erpStatuses, List<String> warehouses, BigDecimal balance) throws UserException {
|
||||
log.info(
|
||||
"Creating an invoice with arguments:\n client ID: {}, shop IDs: {}, period:[{} - {}]",
|
||||
customerId, shopIds.toString(), begin, end
|
||||
|
@ -345,7 +491,7 @@ public class ShippingInvoiceFactory {
|
|||
);
|
||||
uninvoicedOrderToContent = platformOrderService.findUninvoicedOrders(shopIds, begin, end, warehouses);
|
||||
}
|
||||
else if (erpStatuses.toString().equals("[1, 2]")) {
|
||||
else if (erpStatuses.toString().equals("[1, 2]") || erpStatuses.toString().equals("[1]")) {
|
||||
subject = String.format(
|
||||
"Pre-Shipping fees order time from %s to %s",
|
||||
SUBJECT_FORMAT.format(begin),
|
||||
|
@ -361,6 +507,9 @@ public class ShippingInvoiceFactory {
|
|||
);
|
||||
uninvoicedOrderToContent = platformOrderService.findUninvoicedOrderContentsForShopsAndStatus(shopIds, begin, end, erpStatuses, warehouses);
|
||||
}
|
||||
if(balance != null) {
|
||||
return createInvoiceWithBalance(customerId, balance, shopIds, uninvoicedOrderToContent, savRefunds, subject, false);
|
||||
}
|
||||
return createInvoice(customerId, shopIds, uninvoicedOrderToContent, savRefunds, subject, false);
|
||||
}
|
||||
|
||||
|
@ -419,7 +568,7 @@ public class ShippingInvoiceFactory {
|
|||
shops.forEach(shop -> shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee()));
|
||||
String invoiceCode = generateInvoiceCode();
|
||||
log.info("New invoice code: {}", invoiceCode);
|
||||
calculateFees(logisticChannelMap, orderAndContent, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
calculateFees(null, logisticChannelMap, orderAndContent, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
latestDeclaredValues, client, shopServiceFeeMap, shopPackageMatFeeMap, invoiceCode);
|
||||
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
|
||||
if (savRefunds != null) {
|
||||
|
@ -429,6 +578,80 @@ public class ShippingInvoiceFactory {
|
|||
updateOrdersAndContentsInDb(orderAndContent);
|
||||
return invoice;
|
||||
}
|
||||
/**
|
||||
* Creates an invoice based for a client, it's balance, a list of shops, a date range.
|
||||
* <p>
|
||||
* To generate an invoice, it
|
||||
* <ol>
|
||||
* <li>Search orders and their contents based on shop and date range</li>
|
||||
* <li>Generate a new invoice code</li>
|
||||
* <li>Find propre logistic channel price for each order </li>
|
||||
* <li>Update prices of orders and their contents</li>
|
||||
* <li>Generate a invoice</li>
|
||||
* <li>Update invoiced their orders and contents to DB</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param customerId Customer ID
|
||||
* @param balance Balance
|
||||
* @param shopIds Shop IDs
|
||||
* @param subject Invoice subject
|
||||
* @param orderAndContent Map between PlatformOrder and their contents
|
||||
* @param savRefunds List of SAV refunds
|
||||
* @param skipShippingTimeComparing Skip comparing shipping time, true for Pre-shipping, false otherwise
|
||||
* @return the generated invoice
|
||||
* @throws UserException if package used by the invoice can not or find more than 1 logistic
|
||||
* channel price, this exception will be thrown.
|
||||
*/
|
||||
@Transactional
|
||||
public ShippingInvoice createInvoiceWithBalance(String customerId, BigDecimal balance, List<String> shopIds,
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent,
|
||||
List<SavRefundWithDetail> savRefunds,
|
||||
String subject, boolean skipShippingTimeComparing) throws UserException {
|
||||
log.info("Orders to be invoiced: {}", orderAndContent);
|
||||
if (orderAndContent == null || orderAndContent.isEmpty()) {
|
||||
throw new UserException("No platform order in the selected period!");
|
||||
}
|
||||
// TODO : check why invoicing total changes everytime
|
||||
Map<String, BigDecimal> skuRealWeights = new HashMap<>();
|
||||
Map<String, BigDecimal> skuServiceFees = new HashMap<>();
|
||||
skuDataPreparation(skuRealWeights, skuServiceFees);
|
||||
List<Country> countryList = countryService.findAll();
|
||||
Map<LogisticChannel, List<LogisticChannelPrice>> channelPriceMap;
|
||||
Map<String, LogisticChannel> logisticChannelMap = logisticChannelMapper.getAll().stream()
|
||||
.collect(toMap(LogisticChannel::getId, Function.identity()));
|
||||
if(subject.contains("order time")) {
|
||||
channelPriceMap = getChannelPriceMap(logisticChannelMap, orderAndContent, skipShippingTimeComparing, "order");
|
||||
}
|
||||
else {
|
||||
channelPriceMap = getChannelPriceMap(logisticChannelMap, orderAndContent, skipShippingTimeComparing);
|
||||
}
|
||||
List<SkuDeclaredValue> latestDeclaredValues = skuDeclaredValueService.getLatestDeclaredValues();
|
||||
|
||||
Client client = clientMapper.selectById(customerId);
|
||||
List<Shop> shops = shopMapper.selectBatchIds(shopIds);
|
||||
Map<String, BigDecimal> shopServiceFeeMap = new HashMap<>();
|
||||
Map<String, BigDecimal> shopPackageMatFeeMap = new HashMap<>();
|
||||
shops.forEach(shop -> shopServiceFeeMap.put(shop.getId(), shop.getOrderServiceFee()));
|
||||
shops.forEach(shop -> shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee()));
|
||||
String invoiceCode = generateInvoiceCode();
|
||||
log.info("New invoice code: {}", invoiceCode);
|
||||
System.out.println("Order and content size BEFORE : " + orderAndContent.size());
|
||||
Map<String, List<String>> ordersWithPB = calculateFees(balance, logisticChannelMap, orderAndContent, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
latestDeclaredValues, client, shopServiceFeeMap, shopPackageMatFeeMap, invoiceCode);
|
||||
orderAndContent.entrySet().removeIf(entries -> ordersWithPB.containsKey(entries.getKey().getId()));
|
||||
System.out.println("Order and content size AFTER : " + orderAndContent.size());
|
||||
if(orderAndContent.isEmpty()) {
|
||||
log.error("No order was invoiced for customer {} because : {}", client.getInternalCode(), ordersWithPB);
|
||||
throw new UserException("Customer " + customerId + " errors : " + ordersWithPB);
|
||||
}
|
||||
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
|
||||
if (savRefunds != null) {
|
||||
updateSavRefundsInDb(savRefunds, invoiceCode);
|
||||
}
|
||||
ShippingInvoice invoice = new ShippingInvoice(client, invoiceCode, subject, orderAndContent, savRefunds, eurToUsd);
|
||||
updateOrdersAndContentsInDb(orderAndContent);
|
||||
return invoice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a map between LogisticChannel and LogisticChannelPrices, by using distinct country names and
|
||||
|
@ -484,7 +707,7 @@ public class ShippingInvoiceFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private Map<String, List<String>> calculateFees(Map<String, LogisticChannel> logisticChannelMap, Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent,
|
||||
private Map<String, List<String>> calculateFees(BigDecimal balance, Map<String, LogisticChannel> logisticChannelMap, Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent,
|
||||
Map<LogisticChannel, List<LogisticChannelPrice>> channelPriceMap,
|
||||
List<Country> countryList,
|
||||
Map<String, BigDecimal> skuRealWeights,
|
||||
|
@ -496,30 +719,45 @@ public class ShippingInvoiceFactory {
|
|||
String invoiceCode
|
||||
) throws UserException {
|
||||
Map<String, List<String>> platformOrderIdsWithPb = new HashMap<>();
|
||||
// Virtual balance is only used for client type 1 in invoicing job
|
||||
BigDecimal virtualBalance = balance;
|
||||
List<PlatformOrder> insufficientBalanceOrders = new ArrayList<>();
|
||||
boolean skip = false;
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orderContentMap = new HashMap<>(orderAndContent);
|
||||
orderContentMap = orderContentMap.entrySet().stream().sorted(
|
||||
Map.Entry.comparingByKey(Comparator.comparing(PlatformOrder::getOrderTime))
|
||||
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
|
||||
// find logistic channel price for each order based on its content
|
||||
for (PlatformOrder uninvoicedOrder : orderAndContent.keySet()) {
|
||||
List<PlatformOrderContent> contents = orderAndContent.get(uninvoicedOrder);
|
||||
for (PlatformOrder uninvoicedOrder : orderContentMap.keySet()) {
|
||||
if(skip) {
|
||||
if(client.getInternalCode().equals("FT")) {
|
||||
System.out.println("uninvoicedOrder order time : " + uninvoicedOrder.getOrderTime());
|
||||
}
|
||||
platformOrderIdsWithPb.put(uninvoicedOrder.getId(), Collections.singletonList("Skipped"));
|
||||
continue;
|
||||
}
|
||||
List<PlatformOrderContent> contents = orderContentMap.get(uninvoicedOrder);
|
||||
if (contents.isEmpty()) {
|
||||
throw new UserException("Order: {} doesn't have content", uninvoicedOrder.getPlatformOrderId());
|
||||
throw new UserException("Order: {} doesn't have content", uninvoicedOrder.getId());
|
||||
}
|
||||
log.info("Calculating price for {} of order {}", contents, uninvoicedOrder);
|
||||
Map<String, Integer> contentMap = new HashMap<>();
|
||||
Map<String, Integer> contentSkuQtyMap = new HashMap<>();
|
||||
for (PlatformOrderContent content : contents) {
|
||||
String skuId = content.getSkuId();
|
||||
if (contentMap.containsKey(skuId)) {
|
||||
contentMap.put(skuId, contentMap.get(skuId) + content.getQuantity());
|
||||
if (contentSkuQtyMap.containsKey(skuId)) {
|
||||
contentSkuQtyMap.put(skuId, contentSkuQtyMap.get(skuId) + content.getQuantity());
|
||||
} else {
|
||||
contentMap.put(skuId, content.getQuantity());
|
||||
contentSkuQtyMap.put(skuId, content.getQuantity());
|
||||
}
|
||||
}
|
||||
|
||||
// calculate weight of an order
|
||||
Pair<BigDecimal, List<String>> contentWeightResult = platformOrderContentService.calculateWeight(
|
||||
contentMap,
|
||||
contentSkuQtyMap,
|
||||
skuRealWeights
|
||||
);
|
||||
if(!contentWeightResult.getValue().isEmpty()) {
|
||||
platformOrderIdsWithPb.put(uninvoicedOrder.getPlatformOrderId(), contentWeightResult.getValue());
|
||||
platformOrderIdsWithPb.put(uninvoicedOrder.getId(), contentWeightResult.getValue());
|
||||
continue;
|
||||
}
|
||||
BigDecimal contentWeight = contentWeightResult.getKey();
|
||||
|
@ -528,20 +766,16 @@ public class ShippingInvoiceFactory {
|
|||
logisticChannelPair = findAppropriatePrice(countryList, logisticChannelMap,
|
||||
channelPriceMap, uninvoicedOrder, contentWeight);
|
||||
}
|
||||
catch (UserException e) {
|
||||
platformOrderIdsWithPb.put(uninvoicedOrder.getPlatformOrderId(), Collections.singletonList(e.getMessage()));
|
||||
catch (RuntimeException | UserException e) {
|
||||
platformOrderIdsWithPb.put(uninvoicedOrder.getId(), Collections.singletonList(e.getMessage()));
|
||||
continue;
|
||||
}
|
||||
LogisticChannelPrice price = logisticChannelPair.getRight();
|
||||
// update attributes of orders and theirs content
|
||||
|
||||
BigDecimal packageMatFee = shopPackageMatFeeMap.get(uninvoicedOrder.getShopId());
|
||||
if(packageMatFee.compareTo(BigDecimal.ZERO) > 0 && logisticChannelPair.getLeft().getWarehouseInChina().equalsIgnoreCase("0")) {
|
||||
uninvoicedOrder.setPackagingMaterialFee(packageMatFee);
|
||||
}
|
||||
uninvoicedOrder.setFretFee(price.getRegistrationFee());
|
||||
uninvoicedOrder.setPickingFee(price.getAdditionalCost());
|
||||
uninvoicedOrder.setOrderServiceFee(shopServiceFeeMap.get(uninvoicedOrder.getShopId()));
|
||||
uninvoicedOrder.setShippingInvoiceNumber(invoiceCode);
|
||||
BigDecimal fretFee = price.getRegistrationFee();
|
||||
BigDecimal pickingFee = price.getAdditionalCost();
|
||||
BigDecimal orderServiceFee = shopServiceFeeMap.get(uninvoicedOrder.getShopId());
|
||||
BigDecimal totalShippingFee = price.calculateShippingPrice(contentWeight);
|
||||
BigDecimal pickingFeePerItem = price.getPickingFeePerItem();
|
||||
BigDecimal clientVatPercentage = client.getVatPercentage();
|
||||
|
@ -560,6 +794,32 @@ public class ShippingInvoiceFactory {
|
|||
if (vatApplicable && minimumDeclaredValue != null) {
|
||||
totalVAT = calculateTotalVat(totalDeclaredValue, clientVatPercentage, minimumDeclaredValue);
|
||||
}
|
||||
if(virtualBalance != null){
|
||||
virtualBalance = virtualBalance
|
||||
.subtract(packageMatFee)
|
||||
.subtract(fretFee)
|
||||
.subtract(pickingFee)
|
||||
.subtract(orderServiceFee)
|
||||
.subtract(totalShippingFee)
|
||||
.subtract(totalVAT);
|
||||
if (virtualBalance.compareTo(BigDecimal.ZERO) < 0) {
|
||||
if(client.getIsChronologicalOrder().equals("1")) {
|
||||
skip = true;
|
||||
}
|
||||
insufficientBalanceOrders.add(uninvoicedOrder);
|
||||
platformOrderIdsWithPb.put(uninvoicedOrder.getId(), Collections.singletonList("Insufficient balance, order was not invoiced."));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// update attributes of orders and theirs content
|
||||
if(packageMatFee.compareTo(BigDecimal.ZERO) > 0 && logisticChannelPair.getLeft().getWarehouseInChina().equalsIgnoreCase("0")) {
|
||||
uninvoicedOrder.setPackagingMaterialFee(packageMatFee);
|
||||
}
|
||||
uninvoicedOrder.setFretFee(fretFee);
|
||||
uninvoicedOrder.setPickingFee(pickingFee);
|
||||
uninvoicedOrder.setOrderServiceFee(orderServiceFee);
|
||||
uninvoicedOrder.setShippingInvoiceNumber(invoiceCode);
|
||||
// Since we always round up when distributing shipping fees to contents, sometimes the final total sum
|
||||
// is bigger than initial total shipping fee, the remedy is to deduct from the initial total, so we never go
|
||||
// above it
|
||||
|
@ -570,9 +830,167 @@ public class ShippingInvoiceFactory {
|
|||
vatApplicable, pickingFeePerItem, content, remainingShippingFee);
|
||||
}
|
||||
}
|
||||
if(!insufficientBalanceOrders.isEmpty()) {
|
||||
//send mail
|
||||
String emailSubject = "[" + LocalDate.now() + "] Rapport de facturation automatique WIA App";
|
||||
String destEmail = env.getProperty("spring.mail.username");
|
||||
String templateName = "client/confirmedClientsInvoicingJobReport.ftl";
|
||||
Map<String, Object> templateModel = new HashMap<>();
|
||||
templateModel.put("errors", insufficientBalanceOrders);
|
||||
templateModel.put("client", client);
|
||||
templateModel.put("chronologicalOrder", client.getIsChronologicalOrder());
|
||||
try {
|
||||
emailService.newSendSimpleMessage(destEmail, emailSubject, templateName, templateModel);
|
||||
} catch (MessagingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// removing orders that can't be invoiced
|
||||
log.info("Number of orders with problem for client {} : {}", client.getInternalCode(), platformOrderIdsWithPb.size());
|
||||
platformOrderIdsWithPb.keySet().forEach(System.out::println);
|
||||
return platformOrderIdsWithPb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates shipping fees of an order, updates order and contents
|
||||
* and returns the estimated virtual balance
|
||||
* if invoice is complete invoice, we make sure we have enough balance
|
||||
* @param username
|
||||
* @param balance virtual balance
|
||||
* @param logisticChannelMap
|
||||
* @param order
|
||||
* @param contents
|
||||
* @param channelPriceMap
|
||||
* @param countryList
|
||||
* @param skuRealWeights
|
||||
* @param skuServiceFees
|
||||
* @param latestDeclaredValues
|
||||
* @param client
|
||||
* @param shopServiceFeeMap
|
||||
* @param shopPackageMatFeeMap
|
||||
* @param invoiceCode
|
||||
* @return
|
||||
* @throws UserException
|
||||
*/
|
||||
private BigDecimal calculateFeeForOrder(String username,
|
||||
BigDecimal balance,
|
||||
Map<String, LogisticChannel> logisticChannelMap,
|
||||
PlatformOrder order,
|
||||
List<PlatformOrderContent> contents,
|
||||
Map<LogisticChannel, List<LogisticChannelPrice>> channelPriceMap,
|
||||
List<Country> countryList,
|
||||
Map<String, BigDecimal> skuRealWeights,
|
||||
Map<String, BigDecimal> skuServiceFees,
|
||||
List<SkuDeclaredValue> latestDeclaredValues,
|
||||
Client client,
|
||||
Map<String, BigDecimal> shopServiceFeeMap,
|
||||
Map<String, BigDecimal> shopPackageMatFeeMap,
|
||||
String invoiceCode
|
||||
) throws UserException {
|
||||
// Virtual balance is only used for client type 1 in invoicing job
|
||||
BigDecimal virtualBalance = balance;
|
||||
// find logistic channel price for each order based on its content
|
||||
if (contents.isEmpty()) {
|
||||
throw new UserException("Order: {} doesn't have content", order.getPlatformOrderId());
|
||||
}
|
||||
log.info("Calculating price for {} of order {}", contents, order);
|
||||
Map<String, Integer> contentSkuQtyMap = new HashMap<>();
|
||||
for (PlatformOrderContent content : contents) {
|
||||
String skuId = content.getSkuId();
|
||||
if (contentSkuQtyMap.containsKey(skuId)) {
|
||||
contentSkuQtyMap.put(skuId, contentSkuQtyMap.get(skuId) + content.getQuantity());
|
||||
} else {
|
||||
contentSkuQtyMap.put(skuId, content.getQuantity());
|
||||
}
|
||||
}
|
||||
|
||||
// calculate weight of an order
|
||||
Pair<BigDecimal, List<String>> contentWeightResult = platformOrderContentService.calculateWeight(
|
||||
contentSkuQtyMap,
|
||||
skuRealWeights
|
||||
);
|
||||
if(!contentWeightResult.getValue().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
BigDecimal contentWeight = contentWeightResult.getKey();
|
||||
Pair<LogisticChannel, LogisticChannelPrice> logisticChannelPair;
|
||||
try {
|
||||
logisticChannelPair = findAppropriatePrice(countryList, logisticChannelMap,
|
||||
channelPriceMap, order, contentWeight);
|
||||
}
|
||||
catch (UserException e) {
|
||||
log.error(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
LogisticChannelPrice price = logisticChannelPair.getRight();
|
||||
|
||||
BigDecimal packageMatFee = shopPackageMatFeeMap.get(order.getShopId());
|
||||
BigDecimal fretFee = price.getRegistrationFee();
|
||||
BigDecimal pickingFee = price.getAdditionalCost();
|
||||
BigDecimal orderServiceFee = shopServiceFeeMap.get(order.getShopId());
|
||||
BigDecimal totalShippingFee = price.calculateShippingPrice(contentWeight);
|
||||
BigDecimal pickingFeePerItem = price.getPickingFeePerItem();
|
||||
BigDecimal clientVatPercentage = client.getVatPercentage();
|
||||
Map<PlatformOrderContent, BigDecimal> contentDeclaredValueMap = new HashMap<>();
|
||||
BigDecimal totalDeclaredValue = calculateTotalDeclaredValue(contents, contentDeclaredValueMap, latestDeclaredValues);
|
||||
BigDecimal totalVAT = BigDecimal.ZERO;
|
||||
boolean vatApplicable = clientVatPercentage.compareTo(BigDecimal.ZERO) > 0
|
||||
&& EU_COUNTRY_LIST.contains(order.getCountry())
|
||||
// If picking fee per item = 0, it means the package was sent from China so VAT applicable
|
||||
&& price.getPickingFeePerItem().compareTo(BigDecimal.ZERO) == 0;
|
||||
// In case where VAT is applicable, and the transport line has a minimum declared value (MDV) per PACKAGE
|
||||
// We need to first calculate the total declared value and compare it to the MDV
|
||||
// If the total declared value is below the MDV, then the VAT should be calculated with the MDV and
|
||||
// then proportionally applied to each content
|
||||
BigDecimal minimumDeclaredValue = price.getMinimumDeclaredValue();
|
||||
if (vatApplicable && minimumDeclaredValue != null) {
|
||||
totalVAT = calculateTotalVat(totalDeclaredValue, clientVatPercentage, minimumDeclaredValue);
|
||||
}
|
||||
if(virtualBalance != null){
|
||||
virtualBalance = virtualBalance
|
||||
.subtract(packageMatFee)
|
||||
.subtract(fretFee)
|
||||
.subtract(pickingFee)
|
||||
.subtract(orderServiceFee)
|
||||
.subtract(totalShippingFee)
|
||||
.subtract(totalVAT);
|
||||
if (virtualBalance.compareTo(BigDecimal.ZERO) < 0) {
|
||||
return virtualBalance;
|
||||
}
|
||||
// if we are dealing with complete invoice, before inserting orders info
|
||||
// we calcute purchase fee and make sure we have enough balance
|
||||
if(invoiceCode.toCharArray()[8] == '7') {
|
||||
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
|
||||
List<SkuQuantity> skuQuantities = platformOrderContentService.searchOrderContent(Collections.singletonList(order.getId()));
|
||||
List<OrderContentDetail> details = platformOrderService.searchPurchaseOrderDetail(skuQuantities);
|
||||
OrdersStatisticData data = OrdersStatisticData.makeData(details, null);
|
||||
BigDecimal purchaseFee = data.finalAmount();
|
||||
virtualBalance = virtualBalance.subtract(purchaseFee.multiply(eurToUsd));
|
||||
}
|
||||
if (virtualBalance.compareTo(BigDecimal.ZERO) < 0) {
|
||||
return virtualBalance;
|
||||
}
|
||||
}
|
||||
// update attributes of orders and theirs content
|
||||
if(packageMatFee.compareTo(BigDecimal.ZERO) > 0 && logisticChannelPair.getLeft().getWarehouseInChina().equalsIgnoreCase("0")) {
|
||||
order.setPackagingMaterialFee(packageMatFee);
|
||||
}
|
||||
order.setFretFee(fretFee);
|
||||
order.setPickingFee(pickingFee);
|
||||
order.setOrderServiceFee(orderServiceFee);
|
||||
order.setShippingInvoiceNumber(invoiceCode);
|
||||
// Since we always round up when distributing shipping fees to contents, sometimes the final total sum
|
||||
// is bigger than initial total shipping fee, the remedy is to deduct from the initial total, so we never go
|
||||
// above it
|
||||
BigDecimal remainingShippingFee = totalShippingFee;
|
||||
for (PlatformOrderContent content : contents) {
|
||||
remainingShippingFee = calculateAndUpdateContentFees(skuRealWeights, skuServiceFees, order, contentWeight,
|
||||
totalShippingFee, clientVatPercentage, contentDeclaredValueMap, totalDeclaredValue, totalVAT,
|
||||
vatApplicable, pickingFeePerItem, content, remainingShippingFee);
|
||||
}
|
||||
return virtualBalance;
|
||||
}
|
||||
|
||||
private void updateOrdersAndContentsInDb(Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent) {
|
||||
// update them to DB after invoiced
|
||||
platformOrderService.updateBatchById(orderAndContent.keySet());
|
||||
|
@ -801,7 +1219,7 @@ public class ShippingInvoiceFactory {
|
|||
shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee());
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orders = uninvoicedOrdersByShopId.get(shop.getId());
|
||||
try {
|
||||
Map<String, List<String>> orderIdErrorMap = calculateFees(logisticChannelMap, orders, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
Map<String, List<String>> orderIdErrorMap = calculateFees(null, logisticChannelMap, orders, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
latestDeclaredValues, client, shopServiceFeeMap,shopPackageMatFeeMap, null);
|
||||
if(!orderIdErrorMap.isEmpty()) {
|
||||
Map.Entry<String, List<String>> errorEntry = orderIdErrorMap.entrySet().iterator().next();
|
||||
|
@ -858,7 +1276,6 @@ public class ShippingInvoiceFactory {
|
|||
.collect(toMap(LogisticChannel::getId, Function.identity()));
|
||||
Map<LogisticChannel, List<LogisticChannelPrice>> channelPriceMap = getChannelPriceMap(logisticChannelMap, ordersMap, true);
|
||||
|
||||
|
||||
for (Shop shop : shops) {
|
||||
Map<String, BigDecimal> shopServiceFeeMap = new HashMap<>();
|
||||
Map<String, BigDecimal> shopPackageMatFeeMap = new HashMap<>();
|
||||
|
@ -866,24 +1283,10 @@ public class ShippingInvoiceFactory {
|
|||
shopPackageMatFeeMap.put(shop.getId(), shop.getPackagingMaterialFee());
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orders = uninvoicedOrdersByShopId.get(shop.getId());
|
||||
try {
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> ordersCopy = new HashMap<>(orders);
|
||||
Map<String, List<String>> platformOrderIdErrorMap = calculateFees(logisticChannelMap, orders, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
Map<String, List<String>> platformOrderIdErrorMap = calculateFees(null, logisticChannelMap, orders, channelPriceMap, countryList, skuRealWeights, skuServiceFees,
|
||||
latestDeclaredValues, client, shopServiceFeeMap, shopPackageMatFeeMap, null);
|
||||
System.out.println("Error List : " + platformOrderIdErrorMap);
|
||||
for(Map.Entry<PlatformOrder,List<PlatformOrderContent>> entry : orders.entrySet()) {
|
||||
for(Map.Entry<String, List<String>> errorEntry: platformOrderIdErrorMap.entrySet()) {
|
||||
if(entry.getKey().getPlatformOrderId().equals(errorEntry.getKey())) {
|
||||
errorMessages.addAll(errorEntry.getValue());
|
||||
System.out.println("Error List size before : " + platformOrderIdErrorMap.size());
|
||||
System.out.println("Platform Order Id to remove : " + errorEntry.getKey());
|
||||
ordersCopy.remove(entry.getKey());
|
||||
platformOrderIdErrorMap.remove(errorEntry.getKey());
|
||||
System.out.println("Error List size after : " + platformOrderIdErrorMap.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
orders = ordersCopy;
|
||||
platformOrderIdErrorMap.forEach((key, value) -> errorMessages.addAll(value));
|
||||
orders.entrySet().removeIf(entries -> platformOrderIdErrorMap.containsKey(entries.getKey().getId()));
|
||||
List<String> estimationsOrderIds = orders.keySet().stream().map(PlatformOrder::getId).collect(Collectors.toList());
|
||||
BigDecimal eurToUsd = exchangeRatesMapper.getLatestExchangeRate("EUR", "USD");
|
||||
ShippingInvoice invoice = new ShippingInvoice(client, "", "", orders, null, eurToUsd);
|
||||
|
|
|
@ -155,12 +155,6 @@ public class Client implements Serializable {
|
|||
@Excel(name = "公司识别码数值", width = 15)
|
||||
@ApiModelProperty(value = "公司识别码数值")
|
||||
private String companyIdValue;
|
||||
/**
|
||||
* 账户余额
|
||||
*/
|
||||
@Excel(name = "账户余额", width = 15)
|
||||
@ApiModelProperty(value = "账户余额")
|
||||
private BigDecimal balance;
|
||||
/**
|
||||
* IOSS号码
|
||||
*/
|
||||
|
@ -180,10 +174,32 @@ public class Client implements Serializable {
|
|||
@Dict(dicCode = "yn")
|
||||
@ApiModelProperty(value = "是否活跃")
|
||||
private String active;
|
||||
/**
|
||||
* 物流发票是否包含采购
|
||||
* */
|
||||
@Excel(name = "物流发票是否包含采购", width = 15)
|
||||
@ApiModelProperty(value = "物流发票是否包含采购")
|
||||
private java.lang.String isCompleteInvoice;
|
||||
/**
|
||||
* category id
|
||||
* */
|
||||
@Excel(name = "category id", width = 15, dictTable = "client_category", dicText = "name", dicCode = "id")
|
||||
@Dict(dictTable = "client_category", dicText = "name", dicCode = "id")
|
||||
@ApiModelProperty(value = "category id")
|
||||
private java.lang.String clientCategoryId;
|
||||
/**
|
||||
* balance threshold before alert
|
||||
* */
|
||||
@Excel(name = "balance threshold before alert", width = 15)
|
||||
@ApiModelProperty(value = "balance threshold before alert")
|
||||
private java.math.BigDecimal balanceThreshold;
|
||||
/**
|
||||
* invoice in chronological order or first can invoice
|
||||
* */
|
||||
@Excel(name = "invoice in chronological order or first can invoice", width = 15)
|
||||
@ApiModelProperty(value = "invoice in chronological order or first can invoice")
|
||||
private java.lang.String isChronologicalOrder;
|
||||
|
||||
@Excel(name = "是否完整发票", width = 15)
|
||||
@ApiModelProperty(value = "完整发票")
|
||||
private String isCompleteInvoice;
|
||||
public String fullName() {
|
||||
return firstName + " " + surname;
|
||||
}
|
||||
|
|
|
@ -60,4 +60,8 @@ public class ClientCategory implements Serializable {
|
|||
@Excel(name = "description", width = 15)
|
||||
@ApiModelProperty(value = "description")
|
||||
private java.lang.String description;
|
||||
/**balance threshold before alert*/
|
||||
@Excel(name = "balance threshold before alert", width = 15)
|
||||
@ApiModelProperty(value = "balance threshold before alert")
|
||||
private java.math.BigDecimal balanceThreshold;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,9 @@ public class OrderContentDetail {
|
|||
*/
|
||||
public BigDecimal totalPrice() {
|
||||
BigDecimal unit = skuDetail.getPrice().getPrice(quantity, exchangeRate);
|
||||
if(unit == null) {
|
||||
System.out.println("Unit is null : " + skuDetail.getSkuId() + "sku price: " + skuDetail.getPrice() + "quantity: " + quantity);
|
||||
}
|
||||
BigDecimal total = unit.multiply(new BigDecimal(quantity));
|
||||
log.info("unit: {}", unit);
|
||||
log.info("total: {}", total);
|
||||
|
|
|
@ -121,7 +121,7 @@ public class SkuPrice implements Serializable {
|
|||
priceCandidate = priceRmb.divide(eurToRmb, RoundingMode.HALF_UP);
|
||||
discountedPriceCandidate = discountedPriceRmb == null ? priceCandidate : discountedPriceRmb.divide(eurToRmb, RoundingMode.HALF_UP);
|
||||
}
|
||||
if (quantity >= threshold) {
|
||||
if (threshold != null && quantity >= threshold) {
|
||||
return discountedPriceCandidate;
|
||||
}
|
||||
return priceCandidate;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package org.jeecg.modules.business.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.jeecg.modules.business.entity.ClientCategory;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @Description: client category
|
||||
|
@ -12,6 +14,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||
* @Date: 2023-10-19
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Repository
|
||||
public interface ClientCategoryMapper extends BaseMapper<ClientCategory> {
|
||||
|
||||
BigDecimal getBalanceThresholdByCategoryId(@Param("id") String id);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ public interface ClientMapper extends BaseMapper<Client> {
|
|||
|
||||
String getClientEntity(@Param("id") String id);
|
||||
Map<String, String> getClientsEntity(@Param("ids") List<String> ids);
|
||||
String getClientByInternalCode(@Param("code") String code);
|
||||
|
||||
String getClientIdByCode(@Param("code") String code);
|
||||
List<Client> getClientByType(@Param("type") String type);
|
||||
Client getClientByCode(@Param("code") String internalCode);
|
||||
}
|
||||
|
|
|
@ -81,4 +81,5 @@ public interface PlatformOrderContentMapper extends BaseMapper<PlatformOrderCont
|
|||
List<SkuPrice> searchSkuPrice(@Param("skuIds") List<String> skuIds);
|
||||
|
||||
void fetchHighestPriorityAttribute(PlatformOrderContent content);
|
||||
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ public interface PlatformOrderMapper extends BaseMapper<PlatformOrder> {
|
|||
@Param("shops") List<String> shops,
|
||||
@Param("warehouses") List<String> warehouses);
|
||||
|
||||
List<PlatformOrder> fetchUninvoicedShippedOrderIDInShopsAndOrderTime(@Param("startDate") String startDate,
|
||||
List<PlatformOrder> fetchUninvoicedOrderIDInShopsAndOrderTime(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate,
|
||||
@Param("shops") List<String> shops,
|
||||
@Param("erpStatuses") List<Integer> erpStatuses,
|
||||
|
@ -192,4 +192,6 @@ public interface PlatformOrderMapper extends BaseMapper<PlatformOrder> {
|
|||
|
||||
List<PlatformOrder> fetchEmptyLogisticChannelOrders(@Param("startDate") String startDate,@Param("endDate") String endDate);
|
||||
|
||||
void updateErpStatusByCode(@Param("invoiceCode") String invoiceCode, @Param("erpStatus") int erpStatus);
|
||||
|
||||
}
|
||||
|
|
|
@ -91,4 +91,9 @@ public interface PurchaseOrderMapper extends BaseMapper<PurchaseOrder> {
|
|||
String getInvoiceNumber(@Param("purchaseID") String purchaseID);
|
||||
|
||||
|
||||
BigDecimal getPurchaseFeesByInvoiceCode(@Param("invoiceCode") String invoiceCode);
|
||||
|
||||
void deleteInvoice(@Param("invoiceNumber") String invoiceNumber);
|
||||
|
||||
void deleteBatchInvoice(@Param("invoiceNumbers") List<String> invoiceNumbers);
|
||||
}
|
||||
|
|
|
@ -2,10 +2,7 @@ package org.jeecg.modules.business.mapper;
|
|||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.jeecg.modules.business.entity.Client;
|
||||
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||
import org.jeecg.modules.business.entity.PlatformOrderContent;
|
||||
import org.jeecg.modules.business.entity.ShippingInvoice;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -23,4 +20,5 @@ public interface ShippingInvoiceMapper extends BaseMapper<ShippingInvoice> {
|
|||
List<PlatformOrder> fetchPlatformOrder(@Param("invoiceNumber") String invoiceNumber);
|
||||
List<PlatformOrderContent> fetchPlatformOrderContent(@Param("platformOrderId") String platformOrderId);
|
||||
Client fetchShopOwnerFromInvoiceNumber(@Param("invoiceNumber") String invoiceNumber);
|
||||
Currency fetchInvoiceCurrencyByCode(@Param("invoiceNumber") String invoiceCode);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.jeecg.modules.demo.business.mapper.ClientCategoryMapper">
|
||||
|
||||
<mapper namespace="org.jeecg.modules.business.mapper.ClientCategoryMapper">
|
||||
<select id="getBalanceThresholdByCategoryId" resultType="java.math.BigDecimal">
|
||||
SELECT balance_threshold
|
||||
FROM client_category
|
||||
WHERE id = #{id}
|
||||
</select>
|
||||
</mapper>
|
|
@ -14,10 +14,15 @@
|
|||
#{id}
|
||||
</foreach>;
|
||||
</select>
|
||||
<select id="getClientByInternalCode" parameterType="java.lang.String" resultType="java.lang.String">
|
||||
<select id="getClientIdByCode" parameterType="java.lang.String" resultType="java.lang.String">
|
||||
SELECT id
|
||||
FROM client
|
||||
WHERE internal_code = #{code}
|
||||
</select>
|
||||
<select id="getClientByCode" parameterType="java.lang.String" resultType="org.jeecg.modules.business.entity.Client">
|
||||
SELECT *
|
||||
FROM client
|
||||
WHERE internal_code = #{code}
|
||||
</select>
|
||||
<select id="getClientByType" resultType="org.jeecg.modules.business.entity.Client">
|
||||
SELECT c.*
|
||||
|
|
|
@ -295,18 +295,21 @@
|
|||
</foreach>;
|
||||
</insert>
|
||||
<update id="cancelInvoice">
|
||||
UPDATE platform_order_content
|
||||
SET picking_fee = 0.0,
|
||||
shipping_fee = NULL,
|
||||
service_fee = NULL,
|
||||
vat = NULL,
|
||||
purchase_fee = 0.0,
|
||||
erp_status =
|
||||
CASE erp_status
|
||||
WHEN '4' THEN '3'
|
||||
ELSE erp_status
|
||||
UPDATE platform_order_content poc
|
||||
JOIN platform_order po ON poc.platform_order_id = po.id
|
||||
JOIN shipping_invoice si ON po.shipping_invoice_number = si.invoice_number
|
||||
SET poc.picking_fee = 0.0,
|
||||
poc.shipping_fee = NULL,
|
||||
poc.service_fee = NULL,
|
||||
poc.vat = NULL,
|
||||
poc.purchase_fee = 0.0,
|
||||
poc.erp_status =
|
||||
CASE
|
||||
WHEN poc.erp_status = '4' THEN '3'
|
||||
WHEN poc.erp_status = '2' AND si.create_by = 'system' THEN '1'
|
||||
ELSE poc.erp_status
|
||||
END
|
||||
WHERE platform_order_id IN (SELECT id FROM platform_order WHERE shipping_invoice_number = #{invoiceNumber});
|
||||
WHERE po.shipping_invoice_number = #{invoiceNumber};
|
||||
</update>
|
||||
<update id="cancelBatchInvoice">
|
||||
UPDATE platform_order_content
|
||||
|
|
|
@ -88,6 +88,14 @@
|
|||
WHERE id = #{orderID}
|
||||
</update>
|
||||
|
||||
<update id="updateErpStatusByCode">
|
||||
UPDATE platform_order po, platform_order_content poc
|
||||
SET po.erp_status = #{erpStatus},
|
||||
poc.erp_status = #{erpStatus}
|
||||
WHERE po.shipping_invoice_number = #{invoiceCode}
|
||||
AND poc.platform_order_id = po.id;
|
||||
</update>
|
||||
|
||||
<update id="batchUpdateStatus">
|
||||
UPDATE platform_order
|
||||
SET status = #{status}
|
||||
|
@ -488,7 +496,7 @@
|
|||
AND po.shipping_time between #{startDate} AND #{endDate}
|
||||
AND po.erp_status = 3;
|
||||
</select>
|
||||
<select id="fetchUninvoicedShippedOrderIDInShopsAndOrderTime" resultType="org.jeecg.modules.business.entity.PlatformOrder">
|
||||
<select id="fetchUninvoicedOrderIDInShopsAndOrderTime" resultType="org.jeecg.modules.business.entity.PlatformOrder">
|
||||
SELECT po.id
|
||||
FROM platform_order po
|
||||
JOIN logistic_channel lc ON po.logistic_channel_name = lc.zh_name
|
||||
|
@ -643,15 +651,17 @@
|
|||
</insert>
|
||||
|
||||
<update id="cancelInvoice">
|
||||
UPDATE platform_order
|
||||
UPDATE platform_order po
|
||||
JOIN shipping_invoice si ON po.shipping_invoice_number = si.invoice_number
|
||||
SET fret_fee = NULL,
|
||||
order_service_fee = NULL,
|
||||
shipping_invoice_number = NULL,
|
||||
picking_fee = 0.0,
|
||||
packaging_material_fee = 0.0,
|
||||
erp_status =
|
||||
CASE erp_status
|
||||
WHEN '4' THEN '3'
|
||||
CASE
|
||||
WHEN erp_status = '4' THEN '3'
|
||||
WHEN erp_status = '2' AND si.create_by = 'system' THEN '1'
|
||||
ELSE erp_status
|
||||
END
|
||||
WHERE shipping_invoice_number = #{invoiceNumber};
|
||||
|
|
|
@ -63,4 +63,22 @@
|
|||
FROM purchase_order
|
||||
WHERE id = #{purchaseID}
|
||||
</select>
|
||||
<select id="getPurchaseFeesByInvoiceCode" resultType="java.math.BigDecimal">
|
||||
SELECT final_amount
|
||||
FROM purchase_order
|
||||
WHERE invoice_number = #{invoiceCode};
|
||||
</select>
|
||||
<delete id="deleteInvoice">
|
||||
DELETE
|
||||
FROM purchase_order
|
||||
WHERE invoice_number = #{invoiceNumber}
|
||||
</delete>
|
||||
<delete id="deleteBatchInvoice">
|
||||
DELETE
|
||||
FROM purchase_order
|
||||
WHERE invoice_number IN
|
||||
<foreach collection="invoiceNumbers" item="invoiceNumber" index="index" open="(" separator="," close=")">
|
||||
#{invoiceNumber}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
|
@ -29,4 +29,10 @@
|
|||
FROM platform_order_content p
|
||||
WHERE p.platform_order_id = #{platformOrderId}
|
||||
</select>
|
||||
<select id="fetchInvoiceCurrencyByCode" resultType="org.jeecg.modules.business.entity.Currency">
|
||||
SELECT c.id, c.code
|
||||
FROM shipping_invoice s
|
||||
JOIN currency c ON s.currency_id = c.id
|
||||
WHERE invoice_number = #{invoiceNumber}
|
||||
</select>
|
||||
</mapper>
|
|
@ -5,10 +5,12 @@ import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
|||
import javax.mail.MessagingException;
|
||||
import javax.mail.Session;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
public interface EmailService {
|
||||
public void sendSimpleMessage(String recipient, String subject, String text, Session session) throws MessagingException;
|
||||
public void newSendSimpleMessage(String recipient, String subject, String templateName, Map<String, Object> templateModel) throws MessagingException;
|
||||
public void sendMessageWithAttachment(String recipient, String subject, String text, String attachment, Session session) throws MessagingException, IOException;
|
||||
public Properties getMailSender();
|
||||
public FreeMarkerConfigurer freemarkerClassLoaderConfig() throws IOException;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package org.jeecg.modules.business.service;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jeecg.modules.business.entity.Balance;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.business.entity.Client;
|
||||
import org.jeecg.modules.business.vo.BalanceData;
|
||||
import org.jeecg.modules.business.vo.InvoiceMetaData;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
@ -37,4 +40,11 @@ public interface IBalanceService extends IService<Balance> {
|
|||
void editBalance(String operationId, String operationType, String clientId, BigDecimal amount, String currencyId) throws Exception;
|
||||
|
||||
void deleteBatchBalance(List<String> operationIds, String operationType);
|
||||
|
||||
/**
|
||||
* Get low balance clients from client list
|
||||
* @param metaDataList list of meta data
|
||||
* @return
|
||||
*/
|
||||
List<BalanceData> getLowBalanceClients(List<InvoiceMetaData> metaDataList);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public interface IClientService extends IService<Client> {
|
|||
public void delBatchMain (Collection<? extends Serializable> idList);
|
||||
public String getClientEntity(String id);
|
||||
public Map<String, String> getClientsEntity(List<String> ids);
|
||||
public String getClientByInternalCode(String code);
|
||||
public String getClientIdByCode(String code);
|
||||
/**
|
||||
* Get current user's client information
|
||||
* @return client or null if current user's role is not client
|
||||
|
|
|
@ -54,4 +54,5 @@ public interface IPlatformOrderContentService extends IService<PlatformOrderCont
|
|||
* @param invoiceNumbers
|
||||
*/
|
||||
void cancelBatchInvoice(List<String> invoiceNumbers);
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.jeecg.modules.business.service;
|
|||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.business.controller.UserException;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import org.jeecg.modules.business.vo.PlatformOrderQuantity;
|
||||
import org.jeecg.modules.business.vo.SkuQuantity;
|
||||
|
@ -54,19 +55,19 @@ public interface IPlatformOrderService extends IService<PlatformOrder> {
|
|||
|
||||
void processedPlatformOrderPage(IPage<ClientPlatformOrderPage> page);
|
||||
|
||||
OrdersStatisticData getPlatformOrdersStatisticData(List<String> orderIds);
|
||||
OrdersStatisticData getPlatformOrdersStatisticData(List<String> orderIds) throws UserException;
|
||||
|
||||
List<PlatformOrderContent> selectByMainId(String mainId);
|
||||
|
||||
List<ClientPlatformOrderContent> selectClientVersionByMainId(String mainId);
|
||||
|
||||
PurchaseConfirmation confirmPurchaseByPlatformOrder(List<String> platformOrderIdList);
|
||||
PurchaseConfirmation confirmPurchaseByPlatformOrder(List<String> platformOrderIdList) throws UserException;
|
||||
|
||||
PurchaseConfirmation confirmPurchaseBySkuQuantity(List<SkuQuantity> skuIDQuantityMap);
|
||||
PurchaseConfirmation confirmPurchaseBySkuQuantity(List<SkuQuantity> skuIDQuantityMap) throws UserException;
|
||||
|
||||
PurchaseConfirmation confirmPurchaseBySkuQuantity(ClientInfo clientInfo, List<SkuQuantity> skuIDQuantityMap);
|
||||
PurchaseConfirmation confirmPurchaseBySkuQuantity(ClientInfo clientInfo, List<SkuQuantity> skuIDQuantityMap) throws UserException;
|
||||
|
||||
List<OrderContentDetail> searchPurchaseOrderDetail(List<SkuQuantity> skuQuantities);
|
||||
List<OrderContentDetail> searchPurchaseOrderDetail(List<SkuQuantity> skuQuantities) throws UserException;
|
||||
|
||||
OrderQuantity queryOrderQuantities();
|
||||
|
||||
|
@ -199,4 +200,5 @@ public interface IPlatformOrderService extends IService<PlatformOrder> {
|
|||
* @return
|
||||
*/
|
||||
List<PlatformOrder> fetchEmptyLogisticChannelOrders(String startDate, String endDate);
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.jeecg.modules.business.service;
|
|||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.business.controller.UserException;
|
||||
import org.jeecg.modules.business.domain.purchase.invoice.InvoiceData;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import org.jeecg.modules.business.vo.SkuQuantity;
|
||||
|
@ -10,6 +11,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -49,7 +51,7 @@ public interface IPurchaseOrderService extends IService<PurchaseOrder> {
|
|||
*/
|
||||
void setPageForCurrentClient(IPage<PurchaseOrder> page);
|
||||
|
||||
String addPurchase(List<SkuQuantity> skuQuantities);
|
||||
String addPurchase(List<SkuQuantity> skuQuantities) throws UserException;
|
||||
|
||||
/**
|
||||
* Add a new purchase. The purchase contains sku and its quantity indicated by
|
||||
|
@ -65,10 +67,10 @@ public interface IPurchaseOrderService extends IService<PurchaseOrder> {
|
|||
* @return the new purchase order identifier
|
||||
*/
|
||||
@Transactional
|
||||
String addPurchase(List<SkuQuantity> SkuQuantity, List<String> orderIDs);
|
||||
String addPurchase(List<SkuQuantity> SkuQuantity, List<String> orderIDs) throws UserException;
|
||||
|
||||
@Transactional
|
||||
String addPurchase(String username, Client client, String invoiceNumber, List<SkuQuantity> skuQuantities, Map<PlatformOrder, List<PlatformOrderContent>> platformOrderIDs);
|
||||
String addPurchase(String username, Client client, String invoiceNumber, List<SkuQuantity> skuQuantities, Map<PlatformOrder, List<PlatformOrderContent>> platformOrderIDs) throws UserException;
|
||||
|
||||
void savePaymentDocumentForPurchase(String purchaseID, MultipartFile in) throws IOException;
|
||||
|
||||
|
@ -105,4 +107,10 @@ public interface IPurchaseOrderService extends IService<PurchaseOrder> {
|
|||
InvoiceData makeInvoice(String purchaseID) throws IOException, URISyntaxException;
|
||||
|
||||
byte[] getInvoiceByte(String invoiceCode) throws IOException;
|
||||
|
||||
BigDecimal getPurchaseFeesByInvoiceCode(String invoiceCode);
|
||||
|
||||
void cancelInvoice(String invoiceNumber);
|
||||
|
||||
void cancelBatchInvoice(List<String> invoiceNumbers);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package org.jeecg.modules.business.service;
|
||||
|
||||
import org.jeecg.modules.business.entity.Client;
|
||||
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||
import org.jeecg.modules.business.entity.PlatformOrderContent;
|
||||
import org.jeecg.modules.business.entity.ShippingInvoice;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -42,4 +39,5 @@ public interface IShippingInvoiceService extends IService<ShippingInvoice> {
|
|||
public List<PlatformOrder> getPlatformOrder(String invoiceNumber);
|
||||
public List<PlatformOrderContent> getPlatformOrderContent(String platformOrderId);
|
||||
public Client getShopOwnerFromInvoiceNumber(String invoiceNumber);
|
||||
Currency getInvoiceCurrencyByCode(String invoiceCode);
|
||||
}
|
||||
|
|
|
@ -17,9 +17,11 @@ import org.jeecg.modules.business.vo.*;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -52,6 +54,8 @@ public class PlatformOrderShippingInvoiceService {
|
|||
@Autowired
|
||||
ClientMapper clientMapper;
|
||||
@Autowired
|
||||
EmailService emailService;
|
||||
@Autowired
|
||||
ShopMapper shopMapper;
|
||||
@Autowired
|
||||
LogisticChannelPriceMapper logisticChannelPriceMapper;
|
||||
|
@ -81,7 +85,8 @@ public class PlatformOrderShippingInvoiceService {
|
|||
ISavRefundWithDetailService savRefundWithDetailService;
|
||||
@Autowired
|
||||
ISavRefundService savRefundService;
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
@Value("${jeecg.path.shippingTemplatePath_EU}")
|
||||
private String SHIPPING_INVOICE_TEMPLATE_EU;
|
||||
|
||||
|
@ -140,6 +145,8 @@ public class PlatformOrderShippingInvoiceService {
|
|||
}
|
||||
public Period getValidOrderTimePeriod(List<String> shopIDs, List<Integer> erpStatuses) {
|
||||
Date begin = platformOrderMapper.findEarliestUninvoicedPlatformOrderTime(shopIDs, erpStatuses);
|
||||
if(begin == null)
|
||||
return null;
|
||||
ZoneId shanghai = ZoneId.of("Asia/Shanghai");
|
||||
ZoneId paris = ZoneId.of("Europe/Paris");
|
||||
LocalDateTime ldt = LocalDateTime.ofInstant(begin.toInstant(), shanghai);
|
||||
|
@ -164,27 +171,21 @@ public class PlatformOrderShippingInvoiceService {
|
|||
* @throws IOException exception related to invoice file IO.
|
||||
*/
|
||||
@Transactional
|
||||
public InvoiceMetaData makeInvoice(ShippingInvoiceParam param) throws UserException, ParseException, IOException {
|
||||
public InvoiceMetaData makeInvoice(ShippingInvoiceParam param, String ... user) throws UserException, ParseException, IOException {
|
||||
// Creates factory
|
||||
ShippingInvoiceFactory factory = new ShippingInvoiceFactory(
|
||||
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
|
||||
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService);
|
||||
Subject subject = null;
|
||||
try {
|
||||
subject = SecurityUtils.getSubject();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Error while getting subject", e);
|
||||
}
|
||||
String username = subject == null ? "admin" : ((LoginUser) subject.getPrincipal()).getUsername();
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService, emailService, env);
|
||||
String username = user.length > 0 ? user[0] : ((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername();
|
||||
// Creates invoice by factory
|
||||
ShippingInvoice invoice = factory.createInvoice(param.clientID(),
|
||||
param.shopIDs(),
|
||||
param.start(),
|
||||
param.end(),
|
||||
param.getErpStatuses(),
|
||||
param.getWarehouses()
|
||||
param.getWarehouses(),
|
||||
param.getBalance()
|
||||
);
|
||||
// Chooses invoice template based on client's preference on currency
|
||||
return getInvoiceMetaData(username, invoice);
|
||||
|
@ -206,7 +207,7 @@ public class PlatformOrderShippingInvoiceService {
|
|||
ShippingInvoiceFactory factory = new ShippingInvoiceFactory(
|
||||
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
|
||||
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService);
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService, emailService, env);
|
||||
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());
|
||||
|
@ -224,15 +225,15 @@ public class PlatformOrderShippingInvoiceService {
|
|||
* @throws IOException exception related to invoice file IO.
|
||||
*/
|
||||
@Transactional
|
||||
public InvoiceMetaData makeCompleteInvoice(ShippingInvoiceOrderParam param) throws UserException, ParseException, IOException {
|
||||
public InvoiceMetaData makeCompleteInvoice(ShippingInvoiceOrderParam param) throws UserException, ParseException, IOException, MessagingException {
|
||||
// Creates factory
|
||||
ShippingInvoiceFactory factory = new ShippingInvoiceFactory(
|
||||
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
|
||||
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService);
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService, emailService, env);
|
||||
String username = ((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername();
|
||||
// Creates invoice by factory
|
||||
CompleteInvoice invoice = factory.createCompleteShippingInvoice(username, param.clientID(), param.orderIds(), param.getType(), param.getStart(), param.getEnd());
|
||||
CompleteInvoice invoice = factory.createCompleteShippingInvoice(username, param.clientID(), null, param.orderIds(), param.getType(), param.getStart(), param.getEnd());
|
||||
return getInvoiceMetaData(username, invoice);
|
||||
}
|
||||
|
||||
|
@ -246,25 +247,25 @@ public class PlatformOrderShippingInvoiceService {
|
|||
* @throws IOException
|
||||
*/
|
||||
@Transactional
|
||||
public InvoiceMetaData makeCompleteInvoicePostShipping(ShippingInvoiceParam param, String method) throws UserException, ParseException, IOException {
|
||||
public InvoiceMetaData makeCompleteInvoicePostShipping(ShippingInvoiceParam param, String method, String ... user) throws UserException, ParseException, IOException, MessagingException {
|
||||
// Creates factory
|
||||
ShippingInvoiceFactory factory = new ShippingInvoiceFactory(
|
||||
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
|
||||
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService);
|
||||
String username = ((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername();
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService, emailService, env);
|
||||
String username = user.length > 0 ? user[0] : ((LoginUser) SecurityUtils.getSubject().getPrincipal()).getUsername();
|
||||
List<PlatformOrder> platformOrderList;
|
||||
if(method.equals("post")) {
|
||||
//On récupère les commandes entre 2 dates d'expédition avec un status 3
|
||||
platformOrderList = platformOrderMapper.fetchUninvoicedShippedOrderIDInShops(param.getStart(), param.getEnd(), param.shopIDs(), param.getWarehouses());
|
||||
} else {
|
||||
// On récupère les commandes entre 2 dates de commandes avec un status (1,2) ou (1,2,3)
|
||||
platformOrderList = platformOrderMapper.fetchUninvoicedShippedOrderIDInShopsAndOrderTime(param.getStart(), param.getEnd(), param.shopIDs(), param.getErpStatuses(), param.getWarehouses());
|
||||
platformOrderList = platformOrderMapper.fetchUninvoicedOrderIDInShopsAndOrderTime(param.getStart(), param.getEnd(), param.shopIDs(), param.getErpStatuses(), param.getWarehouses());
|
||||
}
|
||||
// on récupère seulement les ID des commandes
|
||||
List<String> orderIds = platformOrderList.stream().map(PlatformOrder::getId).collect(Collectors.toList());
|
||||
// Creates invoice by factory
|
||||
CompleteInvoice invoice = factory.createCompleteShippingInvoice(username, param.clientID(), orderIds, method, param.getStart(), param.getEnd());
|
||||
CompleteInvoice invoice = factory.createCompleteShippingInvoice(username, param.clientID(), param.getBalance() ,orderIds, method, param.getStart(), param.getEnd());
|
||||
return getInvoiceMetaData(username, invoice);
|
||||
}
|
||||
@NotNull
|
||||
|
@ -316,7 +317,7 @@ public class PlatformOrderShippingInvoiceService {
|
|||
ShippingInvoiceFactory factory = new ShippingInvoiceFactory(
|
||||
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
|
||||
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService);
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService, emailService, env);
|
||||
return factory.getEstimations(errorMessages);
|
||||
}
|
||||
|
||||
|
@ -331,7 +332,7 @@ public class PlatformOrderShippingInvoiceService {
|
|||
ShippingInvoiceFactory factory = new ShippingInvoiceFactory(
|
||||
platformOrderService, clientMapper, shopMapper, logisticChannelMapper, logisticChannelPriceMapper,
|
||||
platformOrderContentService, skuDeclaredValueService, countryService, exchangeRatesMapper,
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService);
|
||||
purchaseOrderService, purchaseOrderContentMapper, skuPromotionHistoryMapper, savRefundService, savRefundWithDetailService, emailService, env);
|
||||
return factory.getEstimations(clientId, orderIds, errorMessages);
|
||||
}
|
||||
|
||||
|
@ -483,7 +484,7 @@ public class PlatformOrderShippingInvoiceService {
|
|||
"\nclient : " + entry.getKey() +
|
||||
"\nbetween dates : [" + start + "] --- [" + end + "]");
|
||||
try {
|
||||
ShippingInvoiceParam param = new ShippingInvoiceParam(entry.getKey(), entry.getValue(), start, end, Collections.singletonList(3), Arrays.asList("0", "1"));
|
||||
ShippingInvoiceParam param = new ShippingInvoiceParam(entry.getKey(), null, entry.getValue(), start, end, Collections.singletonList(3), Arrays.asList("0", "1"));
|
||||
InvoiceMetaData metaData;
|
||||
if(invoiceType == 0) {
|
||||
metaData = makeInvoice(param);
|
||||
|
@ -498,6 +499,62 @@ public class PlatformOrderShippingInvoiceService {
|
|||
String internalCode = clientMapper.selectById(entry.getKey()).getInternalCode();
|
||||
invoiceList.add(new InvoiceMetaData("", "error", internalCode, entry.getKey(), e.getMessage()));
|
||||
log.error(e.getMessage());
|
||||
} catch (MessagingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
return invoiceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* make shipping invoice by client and type (shipping or complete) and invoice only if client has sufficient balance
|
||||
* @param balanceDataList list of balance data with client info, balance and currency
|
||||
* @param invoiceType shipping invoice or complete invoice
|
||||
* @return list of filename (invoices and details)
|
||||
*/
|
||||
@Transactional
|
||||
public List<InvoiceMetaData> breakdownInvoiceClientByTypeAndBalance(List<BalanceData> balanceDataList, int invoiceType) {
|
||||
Map<BalanceData, List<String>> clientShopIDsMap = new HashMap<>();
|
||||
List<InvoiceMetaData> invoiceList = new ArrayList<>();
|
||||
for(BalanceData data: balanceDataList) {
|
||||
String id = data.getClient().getId();
|
||||
clientShopIDsMap.put(data, shopService.listIdByClient(id));
|
||||
}
|
||||
for(Map.Entry<BalanceData, List<String>> entry: clientShopIDsMap.entrySet()) {
|
||||
String clientId = entry.getKey().getClient().getId();
|
||||
Period period = getValidOrderTimePeriod(entry.getValue(), Collections.singletonList(1));
|
||||
if(period == null || !period.isValid()) {
|
||||
String internalCode = entry.getKey().getClient().getInternalCode();
|
||||
invoiceList.add(new InvoiceMetaData("", "error", internalCode, clientId, "No order to invoice."));
|
||||
continue;
|
||||
}
|
||||
System.out.println("Period: [" + period.start() + " - " + period.end() + "]");
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(period.start());
|
||||
String start = calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH)+1 < 10 ? "0" : "") + (calendar.get(Calendar.MONTH)+1) + "-" + (calendar.get(Calendar.DAY_OF_MONTH) < 10 ? "0" : "") + (calendar.get(Calendar.DAY_OF_MONTH));
|
||||
calendar.setTime(period.end());
|
||||
String end = calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH)+1 < 10 ? "0" : "") + (calendar.get(Calendar.MONTH)+1) + "-" + (calendar.get(Calendar.DAY_OF_MONTH)+1 < 10 ? "0" : "") + (calendar.get(Calendar.DAY_OF_MONTH)+1);
|
||||
log.info( "Invoicing : " + (invoiceType == 0 ? "Shipping Invoice" : "Complete Shipping Invoice") +
|
||||
"\nclient : " + entry.getKey() +
|
||||
"\nbetween dates : [" + start + "] --- [" + end + "]");
|
||||
try {
|
||||
ShippingInvoiceParam param = new ShippingInvoiceParam(clientId, entry.getKey().getBalance(), entry.getValue(), start, end, Collections.singletonList(1), Arrays.asList("0", "1"));
|
||||
InvoiceMetaData metaData;
|
||||
if(invoiceType == 0) {
|
||||
metaData = makeInvoice(param, "system");
|
||||
balanceService.updateBalance(clientId, metaData.getInvoiceCode(), "shipping");
|
||||
}
|
||||
else {
|
||||
metaData = makeCompleteInvoicePostShipping(param, "pre-shipping", "system");
|
||||
balanceService.updateBalance(clientId, metaData.getInvoiceCode(), "complete");
|
||||
}
|
||||
platformOrderMapper.updateErpStatusByCode(metaData.getInvoiceCode(), 2);
|
||||
invoiceList.add(metaData);
|
||||
} catch (UserException | IOException | ParseException | MessagingException e) {
|
||||
String internalCode = entry.getKey().getClient().getInternalCode();
|
||||
invoiceList.add(new InvoiceMetaData("", "error", internalCode, clientId, e.getMessage()));
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
package org.jeecg.modules.business.service.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.modules.business.entity.Balance;
|
||||
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||
import org.jeecg.modules.business.entity.PlatformOrderContent;
|
||||
import org.jeecg.modules.business.entity.ShippingInvoice;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import org.jeecg.modules.business.mapper.BalanceMapper;
|
||||
import org.jeecg.modules.business.service.IBalanceService;
|
||||
import org.jeecg.modules.business.service.ICurrencyService;
|
||||
import org.jeecg.modules.business.service.IPlatformOrderService;
|
||||
import org.jeecg.modules.business.service.IShippingInvoiceService;
|
||||
import org.jeecg.modules.business.mapper.ClientCategoryMapper;
|
||||
import org.jeecg.modules.business.mapper.ClientMapper;
|
||||
import org.jeecg.modules.business.service.*;
|
||||
import org.jeecg.modules.business.vo.BalanceData;
|
||||
import org.jeecg.modules.business.vo.InvoiceMetaData;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -17,6 +16,7 @@ import org.springframework.stereotype.Service;
|
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -33,11 +33,17 @@ public class BalanceServiceImpl extends ServiceImpl<BalanceMapper, Balance> impl
|
|||
@Autowired
|
||||
private BalanceMapper balanceMapper;
|
||||
@Autowired
|
||||
private ClientCategoryMapper clientCategoryMapper;
|
||||
@Autowired
|
||||
private ClientMapper clientMapper;
|
||||
@Autowired
|
||||
private ICurrencyService currencyService;
|
||||
@Autowired
|
||||
private IPlatformOrderService platformOrderService;
|
||||
@Autowired
|
||||
IShippingInvoiceService iShippingInvoiceService;
|
||||
private IPurchaseOrderService purchaseOrderService;
|
||||
@Autowired
|
||||
IShippingInvoiceService shippingInvoiceService;
|
||||
@Override
|
||||
public BigDecimal getBalanceByClientIdAndCurrency(String clientId, String currency) {
|
||||
return balanceMapper.getBalanceByClientIdAndCurrency(clientId, currency);
|
||||
|
@ -47,20 +53,13 @@ public class BalanceServiceImpl extends ServiceImpl<BalanceMapper, Balance> impl
|
|||
public void updateBalance(String clientId, String invoiceCode, String invoiceType) {
|
||||
|
||||
// balance update
|
||||
ShippingInvoice invoice = iShippingInvoiceService.getShippingInvoice(invoiceCode);
|
||||
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("complete")) {
|
||||
List<String> orderIds = iShippingInvoiceService.getPlatformOrder(invoiceCode).stream().map(PlatformOrder::getId).collect(Collectors.toList());
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orderMap = platformOrderService.fetchOrderData(orderIds);
|
||||
BigDecimal purchaseFees = BigDecimal.ZERO;
|
||||
for(Map.Entry<PlatformOrder, List<PlatformOrderContent>> entry : orderMap.entrySet()) {
|
||||
for(PlatformOrderContent content : entry.getValue()) {
|
||||
purchaseFees = purchaseFees.add(content.getPurchaseFee());
|
||||
}
|
||||
}
|
||||
currentBalance = currentBalance.add(purchaseFees);
|
||||
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);
|
||||
|
@ -86,6 +85,7 @@ public class BalanceServiceImpl extends ServiceImpl<BalanceMapper, Balance> impl
|
|||
public void deleteBatchBalance(List<String> operationIds, String operationType) {
|
||||
balanceMapper.deleteBatchBalance(operationIds, operationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editBalance(String operationId, String operationType, String clientId, BigDecimal amount, String currencyId) throws Exception {
|
||||
log.info("editing balance");
|
||||
|
@ -105,4 +105,20 @@ public class BalanceServiceImpl extends ServiceImpl<BalanceMapper, Balance> impl
|
|||
balanceMapper.insert(newBalance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BalanceData> getLowBalanceClients(List<InvoiceMetaData> metaDataList) {
|
||||
List<BalanceData> lowBalanceDataList = new ArrayList<>();
|
||||
for(InvoiceMetaData metaData : metaDataList) {
|
||||
Client client = clientMapper.getClientByCode(metaData.getInternalCode());
|
||||
Currency currency = shippingInvoiceService.getInvoiceCurrencyByCode(metaData.getInvoiceCode());
|
||||
BigDecimal balance = getBalanceByClientIdAndCurrency(client.getId(), currency.getCode());
|
||||
BigDecimal balanceThreshold = client.getBalanceThreshold() == null ?
|
||||
clientCategoryMapper.getBalanceThresholdByCategoryId(client.getClientCategoryId()) : client.getBalanceThreshold();
|
||||
if(balance.compareTo(balanceThreshold) < 0) {
|
||||
lowBalanceDataList.add(new BalanceData(client, currency.getCode(), balance));
|
||||
}
|
||||
}
|
||||
return lowBalanceDataList;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getClientByInternalCode(String code) {
|
||||
return clientMapper.getClientByInternalCode(code);
|
||||
public String getClientIdByCode(String code) {
|
||||
return clientMapper.getClientIdByCode(code);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,25 +3,32 @@ package org.jeecg.modules.business.service.impl;
|
|||
import freemarker.cache.FileTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.modules.business.service.EmailService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
|
||||
import javax.mail.*;
|
||||
import javax.mail.internet.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class EmailServiceImpl implements EmailService {
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
@Autowired
|
||||
FreeMarkerConfigurer freemarkerConfigurer;
|
||||
@Override
|
||||
@Transactional
|
||||
public Properties getMailSender() {
|
||||
|
@ -49,6 +56,41 @@ public class EmailServiceImpl implements EmailService {
|
|||
|
||||
Transport.send(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void newSendSimpleMessage(String recipient, String subject, String templateName, Map<String, Object> templateModel) {
|
||||
Properties prop = getMailSender();
|
||||
Session session = Session.getInstance(prop, new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(env.getProperty("spring.mail.username"), env.getProperty("spring.mail.password"));
|
||||
}
|
||||
});
|
||||
try {
|
||||
freemarkerConfigurer = freemarkerClassLoaderConfig();
|
||||
Template freemarkerTemplate = freemarkerConfigurer.getConfiguration()
|
||||
.getTemplate(templateName);
|
||||
String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);
|
||||
Message message = new MimeMessage(session);
|
||||
|
||||
message.setFrom(new InternetAddress(Objects.requireNonNull(env.getProperty("spring.mail.username"))));
|
||||
message.setRecipient(Message.RecipientType.TO, InternetAddress.parse(recipient)[0]);
|
||||
if(!recipient.equals(env.getProperty("spring.mail.username")))
|
||||
message.setRecipient(Message.RecipientType.CC, InternetAddress.parse(Objects.requireNonNull(env.getProperty("spring.mail.username")))[0]);
|
||||
|
||||
message.setSubject(subject);
|
||||
message.setContent(htmlBody, "text/html; charset=utf-8");
|
||||
|
||||
Transport.send(message);
|
||||
|
||||
log.info("Mail sent successfully");
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending mail in VipInvoicingJob", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
@Transactional
|
||||
public void sendMessageWithAttachment(String recipient, String subject, String text, String attachment, Session session) throws MessagingException, IOException {
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.modules.business.controller.UserException;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import org.jeecg.modules.business.mapper.ExchangeRatesMapper;
|
||||
import org.jeecg.modules.business.mapper.PlatformOrderContentMapper;
|
||||
|
@ -13,6 +14,7 @@ import org.jeecg.modules.business.service.IClientService;
|
|||
import org.jeecg.modules.business.service.IPlatformOrderService;
|
||||
import org.jeecg.modules.business.service.IShippingFeesWaiverProductService;
|
||||
import org.jeecg.modules.business.vo.PlatformOrderQuantity;
|
||||
import org.jeecg.modules.business.vo.SkuDetail;
|
||||
import org.jeecg.modules.business.vo.SkuQuantity;
|
||||
import org.jeecg.modules.business.vo.SkuShippingFeesWaiver;
|
||||
import org.jeecg.modules.business.vo.clientPlatformOrder.ClientPlatformOrderPage;
|
||||
|
@ -182,7 +184,7 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
|
|||
}
|
||||
|
||||
@Override
|
||||
public OrdersStatisticData getPlatformOrdersStatisticData(List<String> orderIds) {
|
||||
public OrdersStatisticData getPlatformOrdersStatisticData(List<String> orderIds) throws UserException {
|
||||
List<SkuQuantity> skuIDQuantityMap = platformOrderContentMap.searchOrderContent(orderIds);
|
||||
List<OrderContentDetail> data = searchPurchaseOrderDetail(skuIDQuantityMap);
|
||||
return OrdersStatisticData.makeData(data, null);
|
||||
|
@ -200,14 +202,14 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
|
|||
}
|
||||
|
||||
@Override
|
||||
public PurchaseConfirmation confirmPurchaseByPlatformOrder(List<String> platformOrderIdList) {
|
||||
public PurchaseConfirmation confirmPurchaseByPlatformOrder(List<String> platformOrderIdList) throws UserException {
|
||||
List<SkuQuantity> skuIDQuantityMap = platformOrderContentMap.searchOrderContent(platformOrderIdList);
|
||||
return confirmPurchaseBySkuQuantity(skuIDQuantityMap);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PurchaseConfirmation confirmPurchaseBySkuQuantity(List<SkuQuantity> skuIDQuantityMap) {
|
||||
public PurchaseConfirmation confirmPurchaseBySkuQuantity(List<SkuQuantity> skuIDQuantityMap) throws UserException {
|
||||
Client client = clientService.getCurrentClient();
|
||||
ClientInfo clientInfo = new ClientInfo(client);
|
||||
return new PurchaseConfirmation(clientInfo, searchPurchaseOrderDetail(skuIDQuantityMap),
|
||||
|
@ -215,13 +217,13 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
|
|||
}
|
||||
|
||||
@Override
|
||||
public PurchaseConfirmation confirmPurchaseBySkuQuantity(ClientInfo clientInfo, List<SkuQuantity> skuIDQuantityMap) {
|
||||
public PurchaseConfirmation confirmPurchaseBySkuQuantity(ClientInfo clientInfo, List<SkuQuantity> skuIDQuantityMap) throws UserException {
|
||||
return new PurchaseConfirmation(clientInfo, searchPurchaseOrderDetail(skuIDQuantityMap),
|
||||
getShippingFeesWaiverMap(skuIDQuantityMap.stream().map(SkuQuantity::getID).collect(toList())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OrderContentDetail> searchPurchaseOrderDetail(List<SkuQuantity> skuQuantities) {
|
||||
public List<OrderContentDetail> searchPurchaseOrderDetail(List<SkuQuantity> skuQuantities) throws UserException {
|
||||
BigDecimal eurToRmb = exchangeRatesMapper.getLatestExchangeRate("EUR", "RMB");
|
||||
// convert list of (ID, quantity) to map between ID and quantity
|
||||
Map<String, Integer> skuQuantity =
|
||||
|
@ -236,7 +238,14 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
|
|||
// Get list of sku ID
|
||||
List<String> skuList = new ArrayList<>(skuQuantity.keySet());
|
||||
|
||||
List<OrderContentDetail> details = platformOrderContentMap.searchSkuDetail(skuList).stream()
|
||||
List<SkuDetail> skuDetails = platformOrderContentMap.searchSkuDetail(skuList);
|
||||
for(SkuDetail detail : skuDetails) {
|
||||
if(detail.getPrice().getId() == null || detail.getPrice().getPrice() == null) {
|
||||
throw new UserException("SKU " + detail.getSkuId() + " has no price or price id");
|
||||
}
|
||||
}
|
||||
System.out.println("Breakpoint");
|
||||
List<OrderContentDetail> details = skuDetails.stream()
|
||||
.map(
|
||||
skuDetail -> new OrderContentDetail(
|
||||
skuDetail,
|
||||
|
|
|
@ -2,10 +2,7 @@ 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.Client;
|
||||
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||
import org.jeecg.modules.business.entity.PlatformOrderContent;
|
||||
import org.jeecg.modules.business.entity.ShippingInvoice;
|
||||
import org.jeecg.modules.business.entity.*;
|
||||
import org.jeecg.modules.business.mapper.ShippingInvoiceMapper;
|
||||
import org.jeecg.modules.business.service.IShippingInvoiceService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -70,6 +67,11 @@ public class ShippingInvoiceServiceImpl extends ServiceImpl<ShippingInvoiceMappe
|
|||
return shippingInvoiceMapper.fetchShopOwnerFromInvoiceNumber(invoiceNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Currency getInvoiceCurrencyByCode(String invoiceCode) {
|
||||
return shippingInvoiceMapper.fetchInvoiceCurrencyByCode(invoiceCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public String getShippingInvoiceId(String invoiceNumber) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.jeecg.modules.business.service.impl.purchase;
|
|||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.modules.business.controller.UserException;
|
||||
import org.jeecg.modules.business.domain.codeGeneration.PurchaseInvoiceCodeRule;
|
||||
import org.jeecg.modules.business.domain.purchase.invoice.InvoiceData;
|
||||
import org.jeecg.modules.business.domain.purchase.invoice.PurchaseInvoice;
|
||||
|
@ -212,7 +213,7 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
|
|||
* @return the purchase order's identifier (UUID)
|
||||
*/
|
||||
@Override
|
||||
public String addPurchase(List<SkuQuantity> skuQuantities) {
|
||||
public String addPurchase(List<SkuQuantity> skuQuantities) throws UserException {
|
||||
return addPurchase(skuQuantities, Collections.emptyList());
|
||||
}
|
||||
|
||||
|
@ -228,7 +229,7 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
|
|||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public String addPurchase(List<SkuQuantity> skuQuantities, List<String> platformOrderIDs) {
|
||||
public String addPurchase(List<SkuQuantity> skuQuantities, List<String> platformOrderIDs) throws UserException {
|
||||
Objects.requireNonNull(platformOrderIDs);
|
||||
|
||||
Client client = clientService.getCurrentClient();
|
||||
|
@ -318,7 +319,7 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
|
|||
@Override
|
||||
@Transactional
|
||||
public String addPurchase(String username, Client client, String invoiceNumber, List<SkuQuantity> skuQuantities,
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent) {
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orderAndContent) throws UserException {
|
||||
Objects.requireNonNull(orderAndContent);
|
||||
|
||||
List<OrderContentDetail> details = platformOrderService.searchPurchaseOrderDetail(skuQuantities);
|
||||
|
@ -470,4 +471,19 @@ public class PurchaseOrderServiceImpl extends ServiceImpl<PurchaseOrderMapper, P
|
|||
Path invoice = Paths.get(INVOICE_DIR, invoiceCode + ".xlsx");
|
||||
return Files.readAllBytes(invoice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getPurchaseFeesByInvoiceCode(String invoiceCode) {
|
||||
return purchaseOrderMapper.getPurchaseFeesByInvoiceCode(invoiceCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelInvoice(String invoiceNumber) {
|
||||
purchaseOrderMapper.deleteInvoice(invoiceNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelBatchInvoice(List<String> invoiceNumbers) {
|
||||
purchaseOrderMapper.deleteBatchInvoice(invoiceNumbers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package org.jeecg.modules.business.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.jeecg.modules.business.entity.Client;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
public class BalanceData {
|
||||
private final Client client;
|
||||
private final String currency;
|
||||
private final BigDecimal balance;
|
||||
}
|
|
@ -1,16 +1,18 @@
|
|||
package org.jeecg.modules.business.vo;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ShippingInvoiceParam {
|
||||
|
||||
private final String clientID;
|
||||
private final BigDecimal balance;
|
||||
private final List<String> shopIDs;
|
||||
private final String start;
|
||||
private final String end;
|
||||
|
@ -19,12 +21,14 @@ public class ShippingInvoiceParam {
|
|||
private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
public ShippingInvoiceParam(@JsonProperty("clientID") String clientID,
|
||||
@JsonProperty("balance") BigDecimal balance,
|
||||
@JsonProperty("shopIDs") List<String> shopIDs,
|
||||
@JsonProperty("start") String start,
|
||||
@JsonProperty("end") String end,
|
||||
@JsonProperty("erpStatuses") List<Integer> erpStatuses,
|
||||
@JsonProperty("warehouses") List<String> warehouses) {
|
||||
this.clientID = clientID;
|
||||
this.balance = balance;
|
||||
this.shopIDs = shopIDs;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
|
@ -47,16 +51,6 @@ public class ShippingInvoiceParam {
|
|||
public Date end() throws ParseException {
|
||||
return format.parse(end);
|
||||
}
|
||||
public String getStart() {
|
||||
return this.start;
|
||||
}
|
||||
public String getEnd() {
|
||||
return this.end;
|
||||
}
|
||||
public List<Integer> getErpStatuses() { return erpStatuses; }
|
||||
public List<String> getWarehouses() {
|
||||
return warehouses;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ShippingInvoiceParam{" + clientID +
|
||||
|
|
|
@ -1,98 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<table align="center" cellpadding="0" cellspacing="0" width="600" bgcolor="#FFF" style="font-family:Arial,Helvetica,sans-serif;text-align:center;table-layout:fixed;font-size: 16px;border: 1px solid #0B49A6">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="600" height="90" bgcolor="#0B49A6" valign="top" align="center" style="padding:20px 0;table-layout:fixed">
|
||||
<a href="http://app.wia-sourcing.com/user/login">
|
||||
<img src="https://wia-sourcing.com/wp-content/uploads/2022/10/Fichier-24Icons.png" alt="logo" width="360" style="width:100%;max-width:360px;">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<table width="520" align="center" style="color:#000;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher collègue,</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Vous trouverez en pièce-jointe une archive de l'ensemble des factures générées :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Erreurs :</b>
|
||||
<#if errors?size = 0>
|
||||
No error
|
||||
</#if>
|
||||
</td>
|
||||
</tr>
|
||||
<#list errors as error>
|
||||
<tr>
|
||||
<td style="padding:10px 0;">
|
||||
Error: ${error.invoiceEntity} - ${error.errorMsg}
|
||||
</td>
|
||||
</tr>
|
||||
</#list>
|
||||
<tr>
|
||||
<td style="padding:35px 0 5px 0;">Merci d’utiliser nos services.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0;">Cordialement</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0 35px 0;">L’équipe WIA Sourcing.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" bgcolor="#0B49A6" width="600">
|
||||
<table align="center" width="520">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-style: italic;padding: 20px 0;">Ce message a été envoyé automatiquement. Merci de ne pas répondre. Ce message et ainsi que toutes les pièces jointes sont confidentielles.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 0 20px 0;">Si vous avez reçu ce message par erreur, merci de nous avertir immédiatement et de détruire ce message.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Service client :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pour obtenir plus d’informations concernant nos services, veuillez nous contacter à l’adresse ci-dessous ou en visitant notre site web.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520" style="padding: 15px 0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="220" style="text-align:center;border-radius:2em;padding:20px 10px 20px 0;;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/contactez-nous" style="color:white;text-decoration:none">Nous contacter</a></td>
|
||||
<td width="40" ></td>
|
||||
<td width="220" style="text-align:center;border-radius:2em;padding:20px 0 20px 10px;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/" style="color:white;text-decoration:none">Notre site web</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520" style="padding: 0 0 35px 0;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="color:#EF5A1A;">WIA SOURCING</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>© 2018/2023 par WIA Sourcing Agency.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOUS DROITS RÉSERVÉS©</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
<#include "components/header.ftl">
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher collègue,</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Vous trouverez en pièce-jointe une archive de l'ensemble des factures générées :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Erreurs :</b>
|
||||
<#if errors?size = 0>
|
||||
No error
|
||||
</#if>
|
||||
</td>
|
||||
</tr>
|
||||
<#list errors as error>
|
||||
<tr>
|
||||
<td style="padding:10px 0;">
|
||||
Error: ${error.invoiceEntity} - ${error.errorMsg}
|
||||
</td>
|
||||
</tr>
|
||||
</#list>
|
||||
<#include "components/footer.ftl">
|
|
@ -0,0 +1,56 @@
|
|||
<#include "../components/header.ftl">
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher(e) ${client.firstName} ${client.surname},</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Une ou plusieurs commandes n'ont pas pu être facturées en raison d'un solde insuffisant :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<#if errors?size = 0>
|
||||
<p>No error</p>
|
||||
<#elseif chronologicalOrder=="1">
|
||||
<p>Les commandes à partir du numéro ${errors[0].platformOrderNumber} - ${errors[0].orderTime?datetime?string('dd-MM-yyyy')} n'ont pas été facturées.</p>
|
||||
<#else>
|
||||
<table style="border: 1px solid #bbb; text-align: center;width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Numéro de commande</th>
|
||||
<th>Date de commande</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#list errors as error>
|
||||
<tr>
|
||||
<td>${error.platformOrderNumber}</td>
|
||||
<td>${error.orderTime?datetime?string('dd-MM-yyyy')}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
</#if>
|
||||
<#if skipped??>
|
||||
<table style="border: 1px solid #bbb; text-align: center;width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Numéro de commande</th>
|
||||
<th>Date de commande</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#list skipped as error>
|
||||
<tr>
|
||||
<td>${error.platformOrderNumber}</td>
|
||||
<td>${error.orderTime?datetime?string('dd-MM-yyyy')}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
</#if>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Pour continuer à utiliser nos services, nous vous invitons à recharger votre compte.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Pour toute information complémentaire nous vous invitons à vous rapprocher de votre conseiller.</td>
|
||||
</tr>
|
||||
<#include "../components/footer.ftl">
|
|
@ -0,0 +1,16 @@
|
|||
<#include "../components/header.ftl">
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher(e) ${firstname} ${lastname},</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Vous recevez cet e-mail car votre solde sur la plateforme de WIA App est actuellement de <b>${balance} ${currency}</b></td>
|
||||
</tr>
|
||||
<#if clientCategory != "vip">
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Pour continuer à utiliser nos services, nous vous invitons à recharger votre compte.</td>
|
||||
</tr>
|
||||
</#if>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Pour toute information complémentaire nous vous invitons à vous rapprocher de votre conseiller.</td>
|
||||
</tr>
|
||||
<#include "../components/footer.ftl">
|
|
@ -0,0 +1,59 @@
|
|||
<tr>
|
||||
<td style="padding:35px 0 5px 0;">Merci d’utiliser nos services.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0;">Cordialement</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0 35px 0;">L’équipe WIA Sourcing.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" bgcolor="#0B49A6" width="600" style="padding: 20px 0;">
|
||||
<#-- <table align="center" width="520" style="padding: 15px 0">-->
|
||||
<#-- <tbody>-->
|
||||
<#-- <tr>-->
|
||||
<#-- <td width="220" style="text-align:center;border-radius:2em;padding:20px 10px 20px 0;;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/contactez-nous" style="color:white;text-decoration:none">Nous contacter</a></td>-->
|
||||
<#-- <td width="40" ></td>-->
|
||||
<#-- <td width="220" style="text-align:center;border-radius:2em;padding:20px 0 20px 10px;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/" style="color:white;text-decoration:none">Notre site web</a></td>-->
|
||||
<#-- </tr>-->
|
||||
<#-- </tbody>-->
|
||||
<#-- </table>-->
|
||||
<table align="center" width="520">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="color:#EF5A1A;">WIA SOURCING</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>© 2018/2023 par WIA Sourcing Agency.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOUS DROITS RÉSERVÉS©</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520">
|
||||
<tbody style="color: white; font-size: 12px;">
|
||||
<tr>
|
||||
<td style="font-style: italic;padding: 20px 0;">Ce message a été envoyé automatiquement. Merci de ne pas y répondre. <br/>Ce message et ainsi que toutes les pièces jointes sont confidentielles.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 0 20px 0;">Si vous avez reçu ce message par erreur, merci de nous en avertir immédiatement et de détruire ce message.</td>
|
||||
</tr>
|
||||
<#-- <tr>-->
|
||||
<#-- <td>Service client :</td>-->
|
||||
<#-- </tr>-->
|
||||
<#-- <tr>-->
|
||||
<#-- <td>Pour obtenir plus d’informations concernant nos services, veuillez nous contacter à l’adresse ci-dessous ou en visitant notre site web.</td>-->
|
||||
<#-- </tr>-->
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<table align="center" cellpadding="0" cellspacing="0" width="600" bgcolor="#FFF" style="font-family:Arial,Helvetica,sans-serif;text-align:center;table-layout:fixed;font-size: 16px;border: 1px solid #0B49A6">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="600" height="90" bgcolor="#0B49A6" valign="top" align="center" style="padding:20px 0;table-layout:fixed">
|
||||
<a href="http://app.wia-sourcing.com/user/login">
|
||||
<img src="https://wia-sourcing.com/wp-content/uploads/2022/10/Fichier-24Icons.png" alt="logo" width="360" style="width:100%;max-width:360px;">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<table width="520" align="center" style="color:#000;">
|
||||
<tbody>
|
|
@ -1,93 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<table align="center" cellpadding="0" cellspacing="0" width="600" bgcolor="#FFF" style="font-family:Arial,Helvetica,sans-serif;text-align:center;table-layout:fixed;font-size: 16px;border: 1px solid #0B49A6">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="600" height="90" bgcolor="#0B49A6" valign="top" align="center" style="padding:20px 0;table-layout:fixed">
|
||||
<a href="http://app.wia-sourcing.com/user/login">
|
||||
<img src="https://wia-sourcing.com/wp-content/uploads/2022/10/Fichier-24Icons.png" alt="logo" width="360" style="width:100%;max-width:360px;">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<table width="520" align="center" style="color:#000;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher Client(e),</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Vous trouverez en pièce-jointe le fichier que vous nous avez demandé :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Type de fichier :</b> ${fileType}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Client :</b> ${invoiceEntity}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Numéro de facture :</b> <a href="http://app.wia-sourcing.com/business/admin/shippingInvoice/Invoice?invoice=${invoiceNumber}"> ${invoiceNumber} </a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:35px 0 5px 0;">Merci d’utiliser nos services.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0;">Cordialement</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0 35px 0;">L’équipe WIA Sourcing.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" bgcolor="#0B49A6" width="600">
|
||||
<table align="center" width="520">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-style: italic;padding: 20px 0;">Ce message a été envoyé automatiquement. Merci de ne pas répondre. Ce message et ainsi que toutes les pièces jointes sont confidentielles.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 0 20px 0;">Si vous avez reçu ce message par erreur, merci de nous avertir immédiatement et de détruire ce message.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Service client :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pour obtenir plus d’informations concernant nos services, veuillez nous contacter à l’adresse ci-dessous ou en visitant notre site web.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520" style="padding: 15px 0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="220" style="text-align:center;border-radius:2em;padding:20px 10px 20px 0;;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/contactez-nous" style="color:white;text-decoration:none">Nous contacter</a></td>
|
||||
<td width="40" ></td>
|
||||
<td width="220" style="text-align:center;border-radius:2em;padding:20px 0 20px 10px;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/" style="color:white;text-decoration:none">Notre site web</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520" style="padding: 0 0 35px 0;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="color:#EF5A1A;">WIA SOURCING</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>© 2018/2023 par WIA Sourcing Agency.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOUS DROITS RÉSERVÉS©</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>"
|
||||
<#include "components/header.ftl">
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher Client(e),</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Vous trouverez en pièce-jointe le fichier que vous nous avez demandé :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Type de fichier :</b> ${fileType}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Client :</b> ${invoiceEntity}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Numéro de facture :</b> <a href="http://app.wia-sourcing.com/business/admin/shippingInvoice/Invoice?invoice=${invoiceNumber}"> ${invoiceNumber} </a></td>
|
||||
</tr>
|
||||
<#include "components/footer.ftl">
|
|
@ -1,113 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<table align="center" cellpadding="0" cellspacing="0" width="600" bgcolor="#FFF" style="font-family:Arial,Helvetica,sans-serif;text-align:center;table-layout:fixed;font-size: 16px;border: 1px solid #0B49A6">
|
||||
<tbody>
|
||||
<#include "components/header.ftl">
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher(s) collègue(s),</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Des erreurs se sont produites lors de l'attribution d'une ligne de transport à une ou plusieurs commandes :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Erreurs :</b>
|
||||
<#if errors?size = 0>
|
||||
No error
|
||||
</#if>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<table style="border: 1px solid #bbb; text-align: center;">
|
||||
<thead>
|
||||
<tr>
|
||||
<td width="600" height="90" bgcolor="#0B49A6" valign="top" align="center" style="padding:20px 0;table-layout:fixed">
|
||||
<a href="http://app.wia-sourcing.com/user/login">
|
||||
<img src="https://wia-sourcing.com/wp-content/uploads/2022/10/Fichier-24Icons.png" alt="logo" width="360" style="width:100%;max-width:360px;">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<table width="520" align="center" style="color:#000;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher(s) collègue(s),</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Des erreurs se sont produites lors de l'attribution d'une ligne de transport à une ou plusieurs commandes :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Erreurs :</b>
|
||||
<#if errors?size = 0>
|
||||
No error
|
||||
</#if>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<table style="border: 1px solid #bbb; text-align: center;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Shop</th>
|
||||
<th>Order ID</th>
|
||||
<th>Country</th>
|
||||
<th>Sensitive Attribute</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#list errors as error>
|
||||
<tr>
|
||||
<td>${error.shop}</td>
|
||||
<td>${error.orderId}</td>
|
||||
<td>${error.country}</td>
|
||||
<td>${error.sensitiveAttribute}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:35px 0 5px 0;">Merci d’utiliser nos services.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0;">Cordialement</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0 35px 0;">L’équipe WIA Sourcing.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" bgcolor="#0B49A6" width="600">
|
||||
<table align="center" width="520">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-style: italic;padding: 20px 0;">Ce message a été envoyé automatiquement. Merci de ne pas répondre. Ce message et ainsi que toutes les pièces jointes sont confidentielles.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 0 20px 0;">Si vous avez reçu ce message par erreur, merci de nous avertir immédiatement et de détruire ce message.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Service client :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pour obtenir plus d’informations concernant nos services, veuillez nous contacter à l’adresse ci-dessous ou en visitant notre site web.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520" style="padding: 15px 0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="220" style="text-align:center;border-radius:2em;padding:20px 10px 20px 0;;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/contactez-nous" style="color:white;text-decoration:none">Nous contacter</a></td>
|
||||
<td width="40" ></td>
|
||||
<td width="220" style="text-align:center;border-radius:2em;padding:20px 0 20px 10px;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/" style="color:white;text-decoration:none">Notre site web</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520" style="padding: 0 0 35px 0;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="color:#EF5A1A;">WIA SOURCING</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>© 2018/2023 par WIA Sourcing Agency.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOUS DROITS RÉSERVÉS©</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<th>Shop</th>
|
||||
<th>Order ID</th>
|
||||
<th>Country</th>
|
||||
<th>Sensitive Attribute</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#list errors as error>
|
||||
<tr>
|
||||
<td>${error.shop}</td>
|
||||
<td>${error.orderId}</td>
|
||||
<td>${error.country}</td>
|
||||
<td>${error.sensitiveAttribute}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
</tr>
|
||||
<#include "components/footer.ftl">
|
|
@ -1,109 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<table align="center" cellpadding="0" cellspacing="0" width="600" bgcolor="#FFF" style="font-family:Arial,Helvetica,sans-serif;text-align:center;table-layout:fixed;font-size: 16px;border: 1px solid #0B49A6">
|
||||
<#include "components/header.ftl">
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher(s) collègue(s),</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Des erreurs se sont produites lors du déroulement de la tâche plannifiée suivante : <br/><b>${job} invoicing job</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Erreurs :</b>
|
||||
<#if errors?size = 0>
|
||||
No error
|
||||
</#if>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<table style="border: 1px solid #bbb; border-collapse: collapse; text-align: center; width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="border: 1px solid #bbb;">Client</th>
|
||||
<th style="border: 1px solid #bbb;">Error Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#list errors as error>
|
||||
<tr>
|
||||
<td width="600" height="90" bgcolor="#0B49A6" valign="top" align="center" style="padding:20px 0;table-layout:fixed">
|
||||
<a href="http://app.wia-sourcing.com/user/login">
|
||||
<img src="https://wia-sourcing.com/wp-content/uploads/2022/10/Fichier-24Icons.png" alt="logo" width="360" style="width:100%;max-width:360px;">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<table width="520" align="center" style="color:#000;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher(s) collègue(s),</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Des erreurs se sont produites lors du déroulement de la tâche plannifiée : <b>VIP invoicing job</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;"><b>Erreurs :</b>
|
||||
<#if errors?size = 0>
|
||||
No error
|
||||
</#if>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<table style="border: 1px solid #bbb; text-align: center;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Client</th>
|
||||
<th>Error Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#list errors as error>
|
||||
<tr>
|
||||
<td>${error.internalCode}</td>
|
||||
<td>${error.errorMsg}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:35px 0 5px 0;">Merci d’utiliser nos services.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0;">Cordialement</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px 0 35px 0;">L’équipe WIA Sourcing.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" bgcolor="#0B49A6" width="600">
|
||||
<table align="center" width="520">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-style: italic;padding: 20px 0;">Ce message a été envoyé automatiquement. Merci de ne pas répondre. Ce message et ainsi que toutes les pièces jointes sont confidentielles.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 0 20px 0;">Si vous avez reçu ce message par erreur, merci de nous avertir immédiatement et de détruire ce message.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Service client :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pour obtenir plus d’informations concernant nos services, veuillez nous contacter à l’adresse ci-dessous ou en visitant notre site web.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520" style="padding: 15px 0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="220" style="text-align:center;border-radius:2em;padding:20px 10px 20px 0;;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/contactez-nous" style="color:white;text-decoration:none">Nous contacter</a></td>
|
||||
<td width="40" ></td>
|
||||
<td width="220" style="text-align:center;border-radius:2em;padding:20px 0 20px 10px;" bgcolor="#EF5A1A"><a href="https://wia-sourcing.com/" style="color:white;text-decoration:none">Notre site web</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table align="center" width="520" style="padding: 0 0 35px 0;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="color:#EF5A1A;">WIA SOURCING</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>© 2018/2023 par WIA Sourcing Agency.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TOUS DROITS RÉSERVÉS©</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td style="border: 1px solid #bbb;">${error.internalCode}</td>
|
||||
<td style="border: 1px solid #bbb;">${error.errorMsg}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
</tr>
|
||||
<#include "components/footer.ftl">
|
Loading…
Reference in New Issue