1.0.27 根据模板类型为模板变量赋值接口

dev
yinq 1 month ago
parent 377ea9182e
commit 9b4d00a042

@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.oa.base.service.ITemplateVariableAssignService;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -18,7 +19,9 @@ import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.oa.base.domain.vo.BaseTemplateVariableVo;
import org.dromara.oa.base.domain.vo.TemplateVariableAssignVo;
import org.dromara.oa.base.domain.bo.BaseTemplateVariableBo;
import org.dromara.oa.base.domain.bo.TemplateVariableAssignRequest;
import org.dromara.oa.base.service.IBaseTemplateVariableService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@ -36,6 +39,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
public class BaseTemplateVariableController extends BaseController {
private final IBaseTemplateVariableService baseTemplateVariableService;
private final ITemplateVariableAssignService templateVariableAssignService;
/**
*
@ -113,4 +117,17 @@ public class BaseTemplateVariableController extends BaseController {
return R.ok(list);
}
/**
*
*
* @param request ID
* @return
*/
// @SaCheckPermission("oa/base:templateVariable:assign")
@PostMapping("/assign")
public R<List<TemplateVariableAssignVo>> assignTemplateVariables(@Validated @RequestBody TemplateVariableAssignRequest request) {
List<TemplateVariableAssignVo> result = templateVariableAssignService.assignTemplateVariables(request);
return R.ok(result);
}
}

@ -0,0 +1,35 @@
package org.dromara.oa.base.domain.bo;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
*
*
* @author Yinq
* @date 2025-01-XX
*/
@Data
public class TemplateVariableAssignRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* (1 2
*/
@NotNull(message = "模板类型不能为空")
private String templateType;
/**
* ID
*/
@NotNull(message = "合同ID不能为空")
private Long contractId;
}

@ -0,0 +1,40 @@
package org.dromara.oa.base.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
*
*
* @author Yinq
* @date 2025-01-XX
*/
@Data
public class TemplateVariableAssignVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
private String varName;
/**
*
*/
private String varLabel;
/**
*
*/
private Object varValue;
/**
* (1 2 3 4)
*/
private String varType;
}

@ -1,6 +1,5 @@
package org.dromara.oa.base.service;
import org.dromara.oa.base.domain.BaseTemplateVariable;
import org.dromara.oa.base.domain.vo.BaseTemplateVariableVo;
import org.dromara.oa.base.domain.bo.BaseTemplateVariableBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;

@ -0,0 +1,25 @@
package org.dromara.oa.base.service;
import org.dromara.oa.base.domain.bo.TemplateVariableAssignRequest;
import org.dromara.oa.base.domain.vo.TemplateVariableAssignVo;
import java.util.List;
/**
* Service
*
* @author Yinq
* @date 2025-01-XX
*/
public interface ITemplateVariableAssignService {
/**
*
* 1=2=
*
* @param request ID
* @return
*/
List<TemplateVariableAssignVo> assignTemplateVariables(TemplateVariableAssignRequest request);
}

@ -2,12 +2,11 @@ package org.dromara.oa.base.service.impl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.dromara.oa.base.domain.bo.BaseTemplateVariableBo;
@ -16,9 +15,8 @@ import org.dromara.oa.base.domain.BaseTemplateVariable;
import org.dromara.oa.base.mapper.BaseTemplateVariableMapper;
import org.dromara.oa.base.service.IBaseTemplateVariableService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.List;
/**
* Service
@ -39,23 +37,23 @@ public class BaseTemplateVariableServiceImpl implements IBaseTemplateVariableSer
* @return
*/
@Override
public BaseTemplateVariableVo queryById(Long variableId){
public BaseTemplateVariableVo queryById(Long variableId) {
return baseMapper.selectVoById(variableId);
}
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<BaseTemplateVariableVo> queryPageList(BaseTemplateVariableBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<BaseTemplateVariable> lqw = buildQueryWrapper(bo);
Page<BaseTemplateVariableVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<BaseTemplateVariableVo> queryPageList(BaseTemplateVariableBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<BaseTemplateVariable> lqw = buildQueryWrapper(bo);
Page<BaseTemplateVariableVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
@ -70,20 +68,18 @@ public class BaseTemplateVariableServiceImpl implements IBaseTemplateVariableSer
}
private MPJLambdaWrapper<BaseTemplateVariable> buildQueryWrapper(BaseTemplateVariableBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<BaseTemplateVariable> lqw = JoinWrappers.lambda(BaseTemplateVariable.class)
.selectAll(BaseTemplateVariable.class)
.eq(StringUtils.isNotBlank(bo.getVarLabel()), BaseTemplateVariable::getVarLabel, bo.getVarLabel())
.like(StringUtils.isNotBlank(bo.getVarName()), BaseTemplateVariable::getVarName, bo.getVarName())
.eq(StringUtils.isNotBlank(bo.getTemplateType()), BaseTemplateVariable::getTemplateType, bo.getTemplateType())
.eq(StringUtils.isNotBlank(bo.getDataSource()), BaseTemplateVariable::getDataSource, bo.getDataSource())
.eq(StringUtils.isNotBlank(bo.getDataField()), BaseTemplateVariable::getDataField, bo.getDataField())
.eq(StringUtils.isNotBlank(bo.getVarType()), BaseTemplateVariable::getVarType, bo.getVarType())
.eq(StringUtils.isNotBlank(bo.getDefaultValue()), BaseTemplateVariable::getDefaultValue, bo.getDefaultValue())
.eq(bo.getSortOrder() != null, BaseTemplateVariable::getSortOrder, bo.getSortOrder())
.eq(bo.getTemplateId() != null, BaseTemplateVariable::getTemplateId, bo.getTemplateId())
.eq(StringUtils.isNotBlank(bo.getActiveFlag()), BaseTemplateVariable::getActiveFlag, bo.getActiveFlag())
;
.selectAll(BaseTemplateVariable.class)
.eq(StringUtils.isNotBlank(bo.getVarLabel()), BaseTemplateVariable::getVarLabel, bo.getVarLabel())
.like(StringUtils.isNotBlank(bo.getVarName()), BaseTemplateVariable::getVarName, bo.getVarName())
.eq(StringUtils.isNotBlank(bo.getTemplateType()), BaseTemplateVariable::getTemplateType, bo.getTemplateType())
.eq(StringUtils.isNotBlank(bo.getDataSource()), BaseTemplateVariable::getDataSource, bo.getDataSource())
.eq(StringUtils.isNotBlank(bo.getDataField()), BaseTemplateVariable::getDataField, bo.getDataField())
.eq(StringUtils.isNotBlank(bo.getVarType()), BaseTemplateVariable::getVarType, bo.getVarType())
.eq(StringUtils.isNotBlank(bo.getDefaultValue()), BaseTemplateVariable::getDefaultValue, bo.getDefaultValue())
.eq(bo.getSortOrder() != null, BaseTemplateVariable::getSortOrder, bo.getSortOrder())
.eq(bo.getTemplateId() != null, BaseTemplateVariable::getTemplateId, bo.getTemplateId())
.eq(StringUtils.isNotBlank(bo.getActiveFlag()), BaseTemplateVariable::getActiveFlag, bo.getActiveFlag());
return lqw;
}
@ -120,7 +116,7 @@ public class BaseTemplateVariableServiceImpl implements IBaseTemplateVariableSer
/**
*
*/
private void validEntityBeforeSave(BaseTemplateVariable entity){
private void validEntityBeforeSave(BaseTemplateVariable entity) {
//TODO 做一些数据校验,如唯一约束
}
@ -133,7 +129,7 @@ public class BaseTemplateVariableServiceImpl implements IBaseTemplateVariableSer
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;

@ -0,0 +1,403 @@
package org.dromara.oa.base.service.impl;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.oa.base.domain.bo.BaseTemplateVariableBo;
import org.dromara.oa.base.domain.bo.TemplateVariableAssignRequest;
import org.dromara.oa.base.domain.vo.BaseTemplateVariableVo;
import org.dromara.oa.base.domain.vo.TemplateVariableAssignVo;
import org.dromara.oa.base.service.IBaseTemplateVariableService;
import org.dromara.oa.base.service.ITemplateVariableAssignService;
import org.dromara.oa.erp.domain.bo.ErpContractInfoBo;
import org.dromara.oa.erp.domain.bo.ErpContractMaterialBo;
import org.dromara.oa.erp.domain.vo.ErpContractInfoVo;
import org.dromara.oa.erp.domain.vo.ErpContractMaterialVo;
import org.dromara.oa.erp.service.IErpContractInfoService;
import org.dromara.oa.erp.service.IErpContractMaterialService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Service
*
* @author Yinq
* @date 2025-01-XX
*/
@RequiredArgsConstructor
@Service
public class TemplateVariableAssignServiceImpl implements ITemplateVariableAssignService {
private final IBaseTemplateVariableService baseTemplateVariableService;
private final IErpContractInfoService erpContractInfoService;
private final IErpContractMaterialService erpContractMaterialService;
/**
*
* 1=2=
*
* @param request ID
* @return
*/
@Override
public List<TemplateVariableAssignVo> assignTemplateVariables(TemplateVariableAssignRequest request) {
// 根据模板类型选择不同的处理逻辑
String templateType = request.getTemplateType();
if ("1".equals(templateType)) {
// 合同类型
return assignContractTemplateVariables(request);
} else if ("2".equals(templateType)) {
// 发货单类型
return assignDeliveryTemplateVariables(request);
} else {
throw new ServiceException("不支持的模板类型:" + templateType);
}
}
/**
*
*
* @param request
* @return
*/
private List<TemplateVariableAssignVo> assignContractTemplateVariables(TemplateVariableAssignRequest request) {
// 1. 根据模板类型查询所有模板变量配置
BaseTemplateVariableBo queryBo = new BaseTemplateVariableBo();
queryBo.setTemplateType(request.getTemplateType());
List<BaseTemplateVariableVo> allTemplateVariables = baseTemplateVariableService.queryList(queryBo);
// 构建变量名到配置的映射
Map<String, BaseTemplateVariableVo> variableMap = buildVariableMap(allTemplateVariables);
// 2. 查询合同信息
ErpContractInfoBo bo = new ErpContractInfoBo();
bo.setContractId(request.getContractId());
List<ErpContractInfoVo> contractInfoVoList = erpContractInfoService.queryList(bo);
if (contractInfoVoList.isEmpty()) {
throw new ServiceException("合同信息不存在合同ID" + request.getContractId());
}
ErpContractInfoVo contractInfo = contractInfoVoList.get(0);
// 3. 查询合同物料列表(如果存在需要物料数据的变量)
List<ErpContractMaterialVo> contractMaterials = null;
boolean needMaterialData = allTemplateVariables.stream()
.anyMatch(var -> "erp_contract_material".equalsIgnoreCase(var.getDataSource()));
if (needMaterialData) {
ErpContractMaterialBo materialBo = new ErpContractMaterialBo();
materialBo.setContractId(request.getContractId());
contractMaterials = erpContractMaterialService.queryList(materialBo);
for (int i = 0; i < contractMaterials.size(); i++) {
contractMaterials.get(i).setSeq(i + 1L);
}
}
// 4. 为所有模板变量赋值
return buildAssignResultList(allTemplateVariables, variableMap, contractInfo, contractMaterials);
}
/**
*
*
* @param request
* @return
*/
private List<TemplateVariableAssignVo> assignDeliveryTemplateVariables(TemplateVariableAssignRequest request) {
// TODO: 实现发货单模板变量赋值逻辑
throw new ServiceException("发货单模板变量赋值功能暂未实现");
}
/**
*
*
* @param allTemplateVariables
* @param variableMap
* @param contractInfo
* @param contractMaterials
* @return
*/
private List<TemplateVariableAssignVo> buildAssignResultList(
List<BaseTemplateVariableVo> allTemplateVariables,
Map<String, BaseTemplateVariableVo> variableMap,
ErpContractInfoVo contractInfo,
List<ErpContractMaterialVo> contractMaterials) {
List<TemplateVariableAssignVo> resultList = new ArrayList<>();
for (BaseTemplateVariableVo variable : allTemplateVariables) {
if (variable == null || StringUtils.isBlank(variable.getVarName())) {
continue;
}
// 根据 dataSource 决定数据源
Object value = getValueFromDataSource(variable, contractInfo, contractMaterials);
// 如果获取不到值,使用默认值
if (value == null && StringUtils.isNotBlank(variable.getDefaultValue())) {
value = variable.getDefaultValue();
}
// 根据变量类型决定返回类型4表示数组类型
boolean shouldReturnArray = "4".equals(variable.getVarType());
Object finalValue = convertValueToTargetType(value, shouldReturnArray);
// 构建返回对象
TemplateVariableAssignVo assignVo = new TemplateVariableAssignVo();
assignVo.setVarName(variable.getVarName());
assignVo.setVarLabel(variable.getVarLabel());
assignVo.setVarValue(finalValue);
assignVo.setVarType(variable.getVarType());
resultList.add(assignVo);
}
return resultList;
}
/**
*
* #{}
* ^{}
*
* @param varNameWithFormat #{} ^{}
* @return
*/
private VariableNameInfo parseVariableName(String varNameWithFormat) {
if (StringUtils.isBlank(varNameWithFormat)) {
return new VariableNameInfo(varNameWithFormat, false);
}
// 检查是否是 #{变量名} 格式(字符串)
if (varNameWithFormat.startsWith("#{") && varNameWithFormat.endsWith("}")) {
String actualName = varNameWithFormat.substring(2, varNameWithFormat.length() - 1);
return new VariableNameInfo(actualName, false);
}
// 检查是否是 ^{变量名} 格式(数组)
if (varNameWithFormat.startsWith("^{") && varNameWithFormat.endsWith("}")) {
String actualName = varNameWithFormat.substring(2, varNameWithFormat.length() - 1);
return new VariableNameInfo(actualName, true);
}
// 默认格式,返回原值
return new VariableNameInfo(varNameWithFormat, false);
}
/**
*
*/
private static class VariableNameInfo {
private final String actualName;
private final boolean arrayType;
public VariableNameInfo(String actualName, boolean arrayType) {
this.actualName = actualName;
this.arrayType = arrayType;
}
public String getActualName() {
return actualName;
}
public boolean isArrayType() {
return arrayType;
}
}
/**
*
* #{}^{}
*
* @param templateVariables
* @return
*/
private Map<String, BaseTemplateVariableVo> buildVariableMap(
List<BaseTemplateVariableVo> templateVariables) {
Map<String, BaseTemplateVariableVo> variableMap = new HashMap<>();
for (BaseTemplateVariableVo variable : templateVariables) {
String varName = variable.getVarName();
if (StringUtils.isBlank(varName)) {
continue;
}
// 提取变量名(去除占位符)
String normalizedName = normalizeVariableName(varName);
// 添加到映射中使用规范化后的变量名作为key
variableMap.put(normalizedName, variable);
}
return variableMap;
}
/**
*
*
* @param varName #{^{}
* @return
*/
private String normalizeVariableName(String varName) {
if (StringUtils.isBlank(varName)) {
return varName;
}
// 去除 #{变量名} 或 ^{变量名} 格式
if ((varName.startsWith("#{") || varName.startsWith("^{")) && varName.endsWith("}")) {
return varName.substring(2, varName.length() - 1);
}
return varName;
}
/**
*
*
* @param variable
* @param contractInfo
* @param contractMaterials null
* @return
*/
private Object getValueFromDataSource(BaseTemplateVariableVo variable,
ErpContractInfoVo contractInfo,
List<ErpContractMaterialVo> contractMaterials) {
if (StringUtils.isBlank(variable.getDataField())) {
return null;
}
String dataSource = variable.getDataSource();
// 根据数据源类型获取值
if ("erp_contract_material".equalsIgnoreCase(dataSource)) {
// 从合同物料列表中提取字段值
return getFieldValueFromMaterialList(contractMaterials, variable.getDataField());
} else {
// 默认从合同信息中获取
return getFieldValueFromContract(contractInfo, variable.getDataField());
}
}
/**
*
*
* @param contractMaterials
* @param fieldName
* @return
*/
private List<Object> getFieldValueFromMaterialList(List<ErpContractMaterialVo> contractMaterials, String fieldName) {
if (contractMaterials == null || contractMaterials.isEmpty() || StringUtils.isBlank(fieldName)) {
return new ArrayList<>();
}
List<Object> values = new ArrayList<>();
for (ErpContractMaterialVo material : contractMaterials) {
Object value = getFieldValueFromObject(material, fieldName);
// 如果值为null用空字符串代替
values.add(value != null ? value : "");
}
return values;
}
/**
* 使
*
* @param obj
* @param fieldName
* @return
*/
private Object getFieldValueFromObject(Object obj, String fieldName) {
if (obj == null || StringUtils.isBlank(fieldName)) {
return null;
}
try {
// 先尝试使用反射获取字段值
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException e) {
// 如果字段不存在,尝试使用 getter 方法
try {
String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
return obj.getClass().getMethod(methodName).invoke(obj);
} catch (Exception ex) {
// 如果都获取不到,返回 null
return null;
}
} catch (Exception e) {
return null;
}
}
/**
* 使
*
* @param contractInfo
* @param fieldName
* @return
*/
private Object getFieldValueFromContract(ErpContractInfoVo contractInfo, String fieldName) {
return getFieldValueFromObject(contractInfo, fieldName);
}
/**
*
*
* @param value
* @param shouldReturnArray
* @return
*/
private Object convertValueToTargetType(Object value, boolean shouldReturnArray) {
if (shouldReturnArray) {
// 转换为数组类型
if (value instanceof List) {
return value;
} else if (value instanceof Collection) {
return new ArrayList<>((Collection<?>) value);
} else if (value != null) {
// 单个值,转换为列表
List<Object> arrayValue = new ArrayList<>();
arrayValue.add(value);
return arrayValue;
} else {
return new ArrayList<>();
}
} else {
// 转换为字符串类型
if (value instanceof List) {
List<?> list = (List<?>) value;
if (!list.isEmpty()) {
return list.get(0).toString();
} else {
return "";
}
} else if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
if (!collection.isEmpty()) {
return collection.iterator().next().toString();
} else {
return "";
}
} else if (value != null) {
return value.toString();
} else {
return "";
}
}
}
/**
*
*
* @param isArrayType
* @return
*/
private Object getDefaultValueByType(boolean isArrayType) {
return isArrayType ? new ArrayList<>() : "";
}
}

@ -160,6 +160,13 @@ public class ErpContractMaterialVo implements Serializable {
/**
*
*/
@ExcelProperty(value = "计量单位名称")
@ExcelProperty(value = "单位")
private String unitName;
/**
*
*/
@ExcelProperty(value = "序号")
private Long seq;
}

@ -118,6 +118,7 @@ public class ErpContractInfoServiceImpl implements IErpContractInfoService {
.eq(bo.getSignatureAppendix() != null, ErpContractInfo::getSignatureAppendix, bo.getSignatureAppendix())
.eq(bo.getTaxRate() != null, ErpContractInfo::getTaxRate, bo.getTaxRate())
.eq(StringUtils.isNotBlank(bo.getActiveFlag()), ErpContractInfo::getActiveFlag, bo.getActiveFlag())
.eq("t.del_flag", "0")
.orderByDesc(ErpContractInfo::getCreateTime);
return lqw;
}

@ -66,7 +66,7 @@ public class ErpContractMaterialServiceImpl implements IErpContractMaterialServi
@Override
public List<ErpContractMaterialVo> queryList(ErpContractMaterialBo bo) {
MPJLambdaWrapper<ErpContractMaterial> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
return baseMapper.selectCustomErpContractMaterialVoList(lqw);
}
private MPJLambdaWrapper<ErpContractMaterial> buildQueryWrapper(ErpContractMaterialBo bo) {
@ -86,6 +86,7 @@ public class ErpContractMaterialServiceImpl implements IErpContractMaterialServi
.eq(bo.getIncludingPrice() != null, ErpContractMaterial::getIncludingPrice, bo.getIncludingPrice())
.eq(bo.getSubtotal() != null, ErpContractMaterial::getSubtotal, bo.getSubtotal())
.eq(StringUtils.isNotBlank(bo.getActiveFlag()), ErpContractMaterial::getActiveFlag, bo.getActiveFlag())
.eq("t.del_flag", "0")
.orderByAsc(ErpContractMaterial::getContractMaterialId);
return lqw;
}

@ -97,7 +97,9 @@ public class ErpProjectInfoServiceImpl implements IErpProjectInfoService {
.eq(StringUtils.isNotBlank(bo.getFlowStatus()), ErpProjectInfo::getFlowStatus, bo.getFlowStatus())
.eq(bo.getSortOrder() != null, ErpProjectInfo::getSortOrder, bo.getSortOrder())
.eq(bo.getContractId() != null, ErpProjectInfo::getContractId, bo.getContractId())
.eq(StringUtils.isNotBlank(bo.getActiveFlag()), ErpProjectInfo::getActiveFlag, bo.getActiveFlag());
.eq(StringUtils.isNotBlank(bo.getActiveFlag()), ErpProjectInfo::getActiveFlag, bo.getActiveFlag())
.eq("t.del_flag", "0")
.orderByDesc(ErpProjectInfo::getCreateTime);
return lqw;
}

Loading…
Cancel
Save