Commit 34304215 authored by yanghao's avatar yanghao

chore: 客户合并中业绩相关合并

parent 3c999a77
......@@ -68,4 +68,17 @@ public interface CustomerApprovalMapper extends AbstractMapper<CustomerApprovalD
"ORDER BY approval_id DESC limit 1"
})
CustomerApprovalDO getCustomerHandoverApprovalByCustomerId(@Param("customerId") Long customerId);
@Select({
"SELECT * FROM ecw_customer_approval WHERE deleted=0 ",
"AND status = 1 ",
"AND ( ",
"(type = 2 AND details is not null AND details ->> '$.customerHandoverDetailDtoList' is not null and JSON_EXTRACT(details, '$.customerHandoverDetailDtoList') like concat('%\"customerId\": ', concat(#{customerId},',%')))",
" or (customer_id = #{customerId}) ",
") ",
"ORDER BY approval_id DESC"
})
List<CustomerApprovalDO> selectInApprovalByCustomerId(@Param("customerId") Long customerId);
}
......@@ -467,4 +467,7 @@ public interface CustomerService extends IService<CustomerDO> {
* @param isNew 新客户=true,老客户=false
*/
void updateCustomerNewOrOld(Long customerId, Boolean isNew);
void validateCustomerApproval(CustomerDO customerDO);
}
......@@ -8,17 +8,15 @@ import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.apollo.core.event.AssignConfirmedCustomerEvent;
import cn.iocoder.yudao.framework.apollo.core.event.Customer.ReceiveCustomerCalTypeEvent;
import cn.iocoder.yudao.framework.apollo.core.event.AutoProcessNotCustomerServiceExceptionEvent;
import cn.iocoder.yudao.framework.apollo.core.event.Customer.CalculateCustomerTypeEvent;
import cn.iocoder.yudao.framework.apollo.core.event.Customer.ReceiveCustomerCalTypeEvent;
import cn.iocoder.yudao.framework.apollo.core.event.QueryEstimateEnterOpenSeaTimeEvent;
import cn.iocoder.yudao.framework.apollo.core.event.Customer.CalculateCustomerTypeEvent;
import cn.iocoder.yudao.framework.apollo.core.vo.ApplyInfoVO;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.framework.i18n.core.I18nMessage;
......@@ -3911,4 +3909,15 @@ public class CustomerServiceImpl extends AbstractService<CustomerMapper,
.setRemark("手动设置客户业绩类型为【" + (isNew ? "新客户" : "老客户") + "】");
customerOperateLogService.createOperateLog(customerOperateLogCreateReqVO);
}
@Override
public void validateCustomerApproval(CustomerDO customerDO) {
Long customerId = customerDO.getId();
List<CustomerApprovalDO> customerApprovalDOList = customerApprovalService.selectInApprovalByCustomerId(customerId);
if (CollectionUtil.isNotEmpty(customerApprovalDOList)) {
throw exception(CUSTOMER_APPROVAL_IN_PROCESSING);
}
}
}
......@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.mybatis.core.service.IService;
import cn.iocoder.yudao.module.customer.vo.customerApproval.*;
import cn.iocoder.yudao.module.customer.dal.dataobject.customerApproval.CustomerApprovalDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.apache.ibatis.annotations.Param;
/**
* 客户相关审核详情 Service 接口
......@@ -70,4 +71,12 @@ public interface CustomerApprovalService extends IService<CustomerApprovalDO> {
* @return
*/
CustomerApprovalDO getCustomerHandoverApprovalByCustomerId(Long customerId);
/**
* 通过客户id查询所有类型的审批中的审批记录
*
* @param customerId
* @return
*/
List<CustomerApprovalDO> selectInApprovalByCustomerId(Long customerId);
}
......@@ -71,4 +71,9 @@ public class CustomerApprovalServiceImpl extends AbstractService<CustomerApprova
public CustomerApprovalDO getCustomerHandoverApprovalByCustomerId(Long customerId) {
return customerApprovalMapper.getCustomerHandoverApprovalByCustomerId(customerId);
}
@Override
public List<CustomerApprovalDO> selectInApprovalByCustomerId(Long customerId) {
return customerApprovalMapper.selectInApprovalByCustomerId(customerId);
}
}
......@@ -14,9 +14,9 @@ public class CustomerMerge {
@ApiModelProperty(value = "被保留的客户id")
@NotNull(message = "被保留的客户id不能为空")
private Long id1;
private Long customerIdSaved;
@ApiModelProperty(value = "被删除的客户id")
@NotNull(message = "被删除的客户id不能为空")
private Long id2;
private Long customerIdDeleted;
}
......@@ -14,6 +14,13 @@ Content-Type: application/json
### 合并客户
GET {{baseUrl}}/ecw/customer/mergeCus?customerIdSaved=52009&customerIdDeleted=51966
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
Content-Type: application/json
### create
POST {{baseUrl}}/ecw/customer/handover/approval
Authorization: Bearer {{token}}
......
......@@ -61,6 +61,8 @@ import cn.iocoder.yudao.module.ecw.service.internalMessage.InternalMessageServic
import cn.iocoder.yudao.module.ecw.service.paramValid.ParamValidatorService;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
import cn.iocoder.yudao.module.order.dal.dataobject.targetLog.TargetLogDO;
import cn.iocoder.yudao.module.order.service.targetLog.TargetLogService;
import cn.iocoder.yudao.module.product.dal.dataobject.productbrandempower.ProductBrandEmpowerDO;
import cn.iocoder.yudao.module.product.service.productbrandempower.ProductBrandEmpowerServiceImpl;
import cn.iocoder.yudao.module.sale.dal.dataobject.offer.OfferDO;
......@@ -70,10 +72,15 @@ import cn.iocoder.yudao.module.system.api.file.dto.FileMakeReqDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.system.enums.download.DownloadTypeEnum;
import cn.iocoder.yudao.module.wealth.dal.dataobject.commissionPayable.CommissionPayableDO;
import cn.iocoder.yudao.module.wealth.dal.dataobject.commissionPayment.CommissionPaymentDO;
import cn.iocoder.yudao.module.wealth.dal.dataobject.receipt.ReceiptDO;
import cn.iocoder.yudao.module.wealth.service.commissionPayable.CommissionPayableService;
import cn.iocoder.yudao.module.wealth.service.commissionPayment.CommissionPaymentService;
import cn.iocoder.yudao.module.wealth.service.receipt.ReceiptServiceImpl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
......@@ -184,6 +191,15 @@ public class CustomerController {
@Resource
private CustomerOperateLogService customerOperateLogService;
@Resource
private TargetLogService targetLogService;
@Resource
private CommissionPayableService commissionPayableService;
@Resource
private CommissionPaymentService commissionPaymentService;
public CustomerController() {
}
......@@ -393,14 +409,14 @@ public class CustomerController {
// 客户投诉,品牌授权都迁移到保留客户中,其他信息不会迁移,
// 如需要维护非主客户的客户档案信息到保留客户中,请先维护好再操作
CustomerDO customerDO1 = customerService.getById(par.getId1());
CustomerDO customerDO1 = customerService.getById(par.getCustomerIdSaved());
if (customerDO1 == null) {
throw exception(ErrorCodeConstants.CUSTOMER_SAVED_NOT_EXISTS);
}
if (customerDO1.getCustomerService() == null) {
throw exception(ErrorCodeConstants.CUSTOMER_SAVED_CUSTOMER_SERVICE_IS_NULL);
}
CustomerDO customerDO2 = customerService.getById(par.getId2());
CustomerDO customerDO2 = customerService.getById(par.getCustomerIdDeleted());
if (customerDO2 == null) {
throw exception(ErrorCodeConstants.CUSTOMER_DELETED_NOT_EXISTS);
}
......@@ -411,11 +427,14 @@ public class CustomerController {
throw exception(ErrorCodeConstants.CUSTOMER_MERGE_CUSTOMER_SERVICE_NOT_SAME);
}
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
if (doMergeCus(customerDO1, customerDO2, loginUserId))
return success(true);
else
return success(false);
// 验证非主客户是否有客户业务审批中
customerService.validateCustomerApproval(customerDO2);
boolean merge = doMergeCus(customerDO1, customerDO2, SecurityFrameworkUtils.getLoginUserId());
return success(merge);
}
private boolean doMergeCus(CustomerDO customerDO1,
......@@ -443,6 +462,11 @@ public class CustomerController {
非主客户关联的订单/收款单/佣金情况单,佣金付款单有在途流程,
需要测试这种情况是否影响合并
*/
//合并订单 在合并订单前会验证非主客户是否有审批中的订单,若存在则不能合并抛出异常
orderApi.mergeOrder(customerDO1.getId(), customerDO2.getId());
//把客户2的联系方式合并到客户1 1
doMogeContact(customerDO1, customerDO2, loginUserId);
//合并报价单 2
......@@ -451,8 +475,6 @@ public class CustomerController {
doMergeCustomerFollow(customerDO1, customerDO2, loginUserId);
//合并客户投诉 4
doMergeCustomerComplaint(customerDO1, customerDO2, loginUserId);
//合并收款单信息 5
doMergeeReceipt(customerDO1, customerDO2, loginUserId);
//合并产品品牌授权 6
doMergeProductBrandEmpower(customerDO1,customerDO2,loginUserId);
//合并客户登记日志 7
......@@ -461,13 +483,18 @@ public class CustomerController {
customerService.doMergeCustomerCreditLog(customerDO1.getId(),
customerDO2.getId());
//合并订单
orderApi.mergeOrder(customerDO1.getId(), customerDO2.getId());
//合并操作日志 9
// customerService.doMergeCustomerOperateLog(customerDO1.getId(),
// customerDO2.getId());
//合并收款单信息 5
doMergeeReceipt(customerDO1, customerDO2, loginUserId);
// 合并订单佣金应付款
doMergeComissionPayable(customerDO1, customerDO2);
// 合并佣金付款单
doMergeCommissionPayment(customerDO1, customerDO2);
// 合并业绩
doMergeTargetLog(customerDO1, customerDO2);
// 纪录客户合并日志
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
......@@ -484,7 +511,7 @@ public class CustomerController {
.setOrderId(null)
.setOrderNo(null)
.setOperateType(CustomerOperateTypeEnum.CUSTOMER_MERGE.getValue())
.setRemark(String.format("将客户【%d-%s-%s】合并到此客户", customerDO2.getId(), customerDO2.getNumber(), customerDO2.getName()));
.setRemark(String.format("客户合并,删除非主客户【%d-%s-%s】", customerDO2.getId(), customerDO2.getNumber(), customerDO2.getName()));
customerOperateLogService.createOperateLog(customerOperateLogCreateReqVO);
//纪录被删除的客户的日志
......@@ -502,13 +529,34 @@ public class CustomerController {
.setOrderId(null)
.setOrderNo(null)
.setOperateType(CustomerOperateTypeEnum.CUSTOMER_MERGE.getValue())
.setRemark(String.format("将此客户合并到主客户【%d-%s-%s】", customerDO1.getId(), customerDO1.getNumber(), customerDO1.getName()));
.setRemark(String.format("客户合并,将此客户合并到主客户【%d-%s-%s】", customerDO1.getId(), customerDO1.getNumber(), customerDO1.getName()));
customerOperateLogService.createOperateLog(customerOperateLogCreateReqVO2);
return true;
}
private void doMergeCommissionPayment(CustomerDO customerDO1, CustomerDO customerDO2) {
commissionPaymentService.update(new LambdaUpdateWrapper<CommissionPaymentDO>()
.set(CommissionPaymentDO::getCustomerId, customerDO1.getId())
.set(CommissionPaymentDO::getCustomerName, customerDO1.getName())
.eq(CommissionPaymentDO::getCustomerId, customerDO2.getId()));
}
private void doMergeTargetLog(CustomerDO customerDO1, CustomerDO customerDO2) {
targetLogService.update(new LambdaUpdateWrapper<TargetLogDO>()
.set(TargetLogDO::getCustomerId, customerDO1.getId())
.set(TargetLogDO::getCustomerType, customerDO1.getType())
.eq(TargetLogDO::getCustomerId, customerDO2.getId()));
}
private void doMergeComissionPayable(CustomerDO customerDO1, CustomerDO customerDO2) {
commissionPayableService.update(new LambdaUpdateWrapper<CommissionPayableDO>()
.set(CommissionPayableDO::getCustomerId, customerDO1.getId())
.set(CommissionPayableDO::getCustomerName, customerDO1.getName())
.eq(CommissionPayableDO::getCustomerId, customerDO2.getId()));
}
//合并客户信用日志
private void doMergeCustomerLevelLog(CustomerDO customerDO1,
CustomerDO customerDO2,
......@@ -574,6 +622,7 @@ public class CustomerController {
}
}
//合并客户跟进
private void doMergeCustomerFollow(CustomerDO customerDO1,
CustomerDO customerDO2,
......
......@@ -187,6 +187,8 @@ public interface ErrorCodeConstants {
ErrorCode CUSTOMER_MERGE_CUSTOMER_SERVICE_NOT_SAME = new ErrorCode(1004006042, "customer.merge.customer.service.not.same");
ErrorCode CUSTOMER_APPROVAL_IN_PROCESSING = new ErrorCode(1004006043, "customer.approval.in.processing");
}
......@@ -6,7 +6,6 @@ import cn.iocoder.boot.module.order.api.dto.*;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.customer.dal.dataobject.customer.CustomerDO;
import cn.iocoder.yudao.module.customer.service.customer.CustomerService;
import cn.iocoder.yudao.module.order.convert.orderConsignor.OrderConsignorConvert;
import cn.iocoder.yudao.module.order.dal.dataobject.order.OrderDO;
import cn.iocoder.yudao.module.order.dal.dataobject.orderConsignee.OrderConsigneeDO;
import cn.iocoder.yudao.module.order.dal.dataobject.orderConsignor.OrderConsignorDO;
......@@ -15,6 +14,7 @@ import cn.iocoder.yudao.module.order.dal.dataobject.orderObjective.OrderObjectiv
import cn.iocoder.yudao.module.order.service.order.*;
import cn.iocoder.yudao.module.order.service.order.impl.OrderItemServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.base.Joiner;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
......@@ -25,6 +25,9 @@ import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.order.enums.ErrorCodeConstants.ORDER_IS_APPROVAL_IN_PROCESS_WITH_ORDERNOS;
@Service
@Validated
public class OrderApiImpl implements OrderApi {
......@@ -107,6 +110,13 @@ public class OrderApiImpl implements OrderApi {
// 将customerId=customerIdDeleted的订单的customerId更新为customerIdSaved
List<OrderDO> list = orderService.selectList(new LambdaQueryWrapperX<OrderDO>().eq(OrderDO::getCustomerId, customerIdDeleted));
if (CollectionUtil.isNotEmpty(list)) {
// 订单有审核中,则不允许合并
List<String> auditingOrderNoList = list.stream().filter(t -> t.getAuditType() != 0).map(OrderDO::getOrderNo).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(auditingOrderNoList)) {
throw exception(ORDER_IS_APPROVAL_IN_PROCESS_WITH_ORDERNOS, Joiner.on(",").join(auditingOrderNoList));
}
for (OrderDO order : list) {
order.setCustomerId(customerIdSaved);
order.setUpdateTime(new Date());
......
......@@ -152,7 +152,6 @@ public interface ErrorCodeConstants {
ErrorCode YOUR_CUSTOMER_INFO_IS_INCOMPLETE = new ErrorCode(1004020105, "your.customer.information.is.incomplete");
ErrorCode ORDER_IS_APPROVAL_IN_PROCESS = new ErrorCode(1004020106, "order.is.approval.in.process");
ErrorCode MODIFY_APPROVAL_NOT_UPDATE_OR_DELETE_WAREHOUSE_IN = new ErrorCode(1004020107, "modify.approval.not.update.warehouse.in");
ErrorCode WAREHOUSE_IN_ITEM_EXPRESS_NO_LIMIT = new ErrorCode(1004020108, "warehouse.in.item.expressno.limit");
......@@ -417,4 +416,7 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_IS_APPROVAL_IN_PROCESS_WITH_ORDERNOS = new ErrorCode(1004001167, "order.is.approval.in.process.with.ordernos");
}
......@@ -293,4 +293,6 @@ order.sorting.not.shipment.not.update=
box.update.repeat.commit=
\ No newline at end of file
box.update.repeat.commit=
order.is.approval.in.process.with.ordernos=
customer.approval.in.processing=
\ No newline at end of file
......@@ -1085,7 +1085,7 @@ customer.saved.not.exists=customer saved not exists
customer.deleted.not.exists=customer deleted not exists
customer.saved.customer.service.is.null=customer saved customer service is null
customer.deleted.customer.service.is.null=customer merged customer service is null
customer.merge.customer.service.not.same=tow customer's customer serivce not same
customer.merge.customer.service.not.same=Retained clients and non-primary clients cannot be under two different account managers
......@@ -1099,4 +1099,6 @@ order.pre.installation.unpackaged.container.not.update=Order pre installed witho
order.sorting.not.shipment.not.update=Order sorting not shipped, order information cannot be modified
box.update.repeat.commit=Duplicate submission of self assigned number status modification
\ No newline at end of file
box.update.repeat.commit=Duplicate submission of self assigned number status modification
order.is.approval.in.process.with.ordernos=Orders placed by non-main customers are being approved and cannot be merged: [{}]
customer.approval.in.processing=Non-main customer approval is in progress and cannot be operated
\ No newline at end of file
......@@ -1085,17 +1085,19 @@ customer.saved.not.exists=\u4FDD\u7559\u5BA2\u6237\u4E0D\u5B58\u5728
customer.deleted.not.exists=\u88AB\u5408\u5E76\u5BA2\u6237\u4E0D\u5B58\u5728
customer.saved.customer.service.is.null=\u88AB\u4FDD\u7559\u5BA2\u6237\u7684\u5BA2\u6237\u7ECF\u7406\u4E3A\u7A7A
customer.deleted.customer.service.is.null=\u88AB\u5408\u5E76\u7684\u5BA2\u6237\u7684\u5BA2\u6237\u7ECF\u7406\u4E3A\u7A7A
customer.merge.customer.service.not.same=\u4E24\u5BA2\u6237\u7684\u5BA2\u6237\u7ECF\u7406\u4E0D\u4E00\u81F4
customer.merge.customer.service.not.same=\u4FDD\u7559\u5BA2\u6237\u548C\u975E\u4E3B\u5BA2\u6237\uFF0C\u4E0D\u80FD\u5728\u4E24\u4E2A\u4E0D\u540C\u7684\u5BA2\u6237\u7ECF\u7406\u540D\u4E0B
order.exists.pickup.not.cargo.control=\u8ba2\u5355\u5b58\u5728\u63d0\u8d27\u8bb0\u5f55\uff0c\u4e0d\u80fd\u8fdb\u884c\u63a7\u8d27
order.exists.pick.not.cancel.cargo.control=\u8ba2\u5355\u5b58\u5728\u653e\u8d27\u8bb0\u5f55\uff0c\u4e0d\u80fd\u53d6\u6d88\u63a7\u8d27
order.overseas.warehouse.update.need.applying=\u8ba2\u5355\u6d77\u5916\u4ed3\u4fee\u6539\u9700\u8981\u5355\u72ec\u7533\u8bf7
order.not.update.dest.country=\u4e0d\u5141\u8bb8\u4fee\u6539\u76ee\u7684\u56fd
order.not.update.departure=\u4e0d\u5141\u8bb8\u4fee\u6539\u59cb\u53d1\u5730
order.not.update.transport=\u4e0d\u5141\u8bb8\u4fee\u6539\u8fd0\u8f93\u65b9\u5f0f
order.pre.installation.unpackaged.container.not.update=\u8ba2\u5355\u9884\u88c5\u672a\u88c5\u67dc, \u8ba2\u5355\u4fe1\u606f\u65e0\u6cd5\u4fee\u6539
order.sorting.not.shipment.not.update=\u8ba2\u5355\u5206\u62e3\u672a\u51fa\u8d27, \u8ba2\u5355\u4fe1\u606f\u65e0\u6cd5\u4fee\u6539
order.exists.pickup.not.cargo.control=\u8BA2\u5355\u5B58\u5728\u63D0\u8D27\u8BB0\u5F55\uFF0C\u4E0D\u80FD\u8FDB\u884C\u63A7\u8D27
order.exists.pick.not.cancel.cargo.control=\u8BA2\u5355\u5B58\u5728\u653E\u8D27\u8BB0\u5F55\uFF0C\u4E0D\u80FD\u53D6\u6D88\u63A7\u8D27
order.overseas.warehouse.update.need.applying=\u8BA2\u5355\u6D77\u5916\u4ED3\u4FEE\u6539\u9700\u8981\u5355\u72EC\u7533\u8BF7
order.not.update.dest.country=\u4E0D\u5141\u8BB8\u4FEE\u6539\u76EE\u7684\u56FD
order.not.update.departure=\u4E0D\u5141\u8BB8\u4FEE\u6539\u59CB\u53D1\u5730
order.not.update.transport=\u4E0D\u5141\u8BB8\u4FEE\u6539\u8FD0\u8F93\u65B9\u5F0F
order.pre.installation.unpackaged.container.not.update=\u8BA2\u5355\u9884\u88C5\u672A\u88C5\u67DC, \u8BA2\u5355\u4FE1\u606F\u65E0\u6CD5\u4FEE\u6539
order.sorting.not.shipment.not.update=\u8BA2\u5355\u5206\u62E3\u672A\u51FA\u8D27, \u8BA2\u5355\u4FE1\u606F\u65E0\u6CD5\u4FEE\u6539
box.update.repeat.commit=\u81ea\u7f16\u53f7\u72b6\u6001\u4fee\u6539\u91cd\u590d\u63d0\u4ea4
\ No newline at end of file
box.update.repeat.commit=\u81EA\u7F16\u53F7\u72B6\u6001\u4FEE\u6539\u91CD\u590D\u63D0\u4EA4
order.is.approval.in.process.with.ordernos=\u975E\u4E3B\u5BA2\u6237\u4E0B\u6709\u8BA2\u5355\u6B63\u5728\u5BA1\u6279\u4E2D\uFF0C\u4E0D\u5141\u8BB8\u5408\u5E76\uFF1A[{}]
customer.approval.in.processing=\u975E\u4E3B\u5BA2\u6237\u5BA1\u6279\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u4E0D\u5141\u8BB8\u5408\u5E76
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment