add task and plan

plp
chenshuichuan 2 years ago
parent a2a2b50ac7
commit 8a3eec1373

@ -0,0 +1,9 @@
字典标签
字典键值
入库 6
完工 5
开工 4
计划 3
下达 2
送审 1
草稿 0

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.erp.service.sale;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import static org.junit.jupiter.api.Assertions.*;
@Import(ErpSaleOrderServiceImpl.class)
class ErpSaleOrderServiceImplTest extends BaseDbUnitTest {
@Test
void createSaleOrder() {
}
@Test
void getSaleOrder() {
}
}

@ -21,7 +21,6 @@ public class PlanSaveReqVO {
private Long productId;
@Schema(description = "任务单明细ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "18938")
@NotNull(message = "任务单明细ID不能为空")
private Long taskDetailId;
@Schema(description = "任务单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "18331")
@ -36,7 +35,6 @@ public class PlanSaveReqVO {
private Long finishNumber;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "计划开始时间", requiredMode = Schema.RequiredMode.REQUIRED)

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.controller.admin.task;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.ViewTaskProductSummary;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
@ -71,7 +72,37 @@ public class TaskController {
TaskDO task = taskService.getTask(id);
return success(BeanUtils.toBean(task, TaskRespVO.class));
}
@GetMapping("/getTaskList")
@Operation(summary = "获得可查询计划的生产任务单列表")
public CommonResult<List<TaskDO>> getTaskList() {
List<Integer> statusList = new ArrayList<>();
statusList.add(TaskStatusEnum..getValue());
statusList.add(TaskStatusEnum..getValue());
statusList.add(TaskStatusEnum..getValue());
statusList.add(TaskStatusEnum..getValue());
statusList.add(TaskStatusEnum..getValue());
List<TaskDO> taskDOList = taskService.selectList(statusList);
return success(taskDOList);
}
@GetMapping("/getPlanTaskList")
@Operation(summary = "获得可做计划的生产任务单列表")
public CommonResult<List<TaskDO>> getPlanTaskList() {
List<Integer> statusList = new ArrayList<>();
statusList.add(TaskStatusEnum..getValue());
statusList.add(TaskStatusEnum..getValue());
statusList.add(TaskStatusEnum..getValue());
List<TaskDO> taskDOList = taskService.selectList(statusList);
return success(taskDOList);
}
@GetMapping("/pagePlanTask")
@Operation(summary = "获得可安排计划的任务单分页")
@PreAuthorize("@ss.hasPermission('mes:task:query')")
public CommonResult<PageResult<TaskRespVO>> pagePlanTask(@Valid TaskPageReqVO pageReqVO) {
PageResult<TaskDO> pageResult = taskService.getTaskPage2(pageReqVO);
return success(BeanUtils.toBean(pageResult, TaskRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得生产任务单分页")
@PreAuthorize("@ss.hasPermission('mes:task:query')")
@ -144,5 +175,11 @@ public class TaskController {
public CommonResult<TaskDetailDO> getTaskDetail(@RequestParam("id") Long id) {
return success(taskService.getTaskDetail(id));
}
@GetMapping("/task-detail/summary")
@Operation(summary = "获得生产任务单汇总明细")
@Parameter(name = "taskId", description = "task ID")
@PreAuthorize("@ss.hasPermission('mes:task:query')")
public CommonResult<List<ViewTaskProductSummary>> getTaskDetailSummary(@RequestParam("taskId") Long taskId) {
return success(taskService.getTaskProductSummaryList(taskId));
}
}

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.controller.admin.task.vo;
import cn.hutool.core.util.ObjUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum TaskStatusEnum {
稿(0),
(1),
(2),
(3),
(4),
(5),
(6);
private final int value;
// 一个可选的方法,用于根据整数值获取对应的枚举实例
public static TaskStatusEnum fromValue(int value) {
for (TaskStatusEnum status : TaskStatusEnum.values()) {
if (status.getValue() == value) {
return status;
}
}
throw new IllegalArgumentException("Unknown value: " + value);
}
}

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.task;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
* DO
*
* @author
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TaskDetailSumResult {
/**
* ID
*/
private Long productId;
/**
* task ID
*/
private Long taskId;
/**
*
*/
private Long number;
}

@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.task;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
* DO
*
* @author
*/
@TableName("mes_view_task_product_summary")
@KeySequence("mes_view_task_product_summary_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ViewTaskProductSummary {
/**
* ID
*/
private Long productId;
/**
* ID
*/
private String productName;
/**
* task ID
*/
private Long taskId;
/**
* task ID
*/
private String taskCode;
/**
*
*/
private Long totalNumber;
/**
*
*/
private Long planNumber;
}

@ -1,12 +1,17 @@
package cn.iocoder.yudao.module.mes.dal.mysql.plan;
import java.math.BigDecimal;
import java.util.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.mes.controller.admin.plan.vo.*;
@ -40,4 +45,18 @@ public interface PlanMapper extends BaseMapperX<PlanDO> {
default PlanDO selectByNo(String no) {
return selectOne(PlanDO::getCode, no);
}
default Long selectSum(Long taskId, Long productId, List<Integer>statusList) {
// SQL sum 查询
List<Map<String, Object>> result = selectMaps(new QueryWrapper<PlanDO>()
.select("SUM(plan_number) AS sumCount")
.in(statusList!=null&&statusList.size()>0,"status", statusList)
.eq(taskId!=null,"task_id", taskId)
.eq(productId!=null,"product_id", productId));
// 获得数量
if (CollUtil.isEmpty(result)) {
return 0L;
}
return MapUtil.getLong(result.get(0), "sumCount", 0L);
}
}

@ -1,12 +1,19 @@
package cn.iocoder.yudao.module.mes.dal.mysql.task;
import java.math.BigDecimal;
import java.util.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailSumResult;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
/**
@ -29,5 +36,5 @@ public interface TaskDetailMapper extends BaseMapperX<TaskDetailDO> {
.eq(TaskDetailDO::getTaskId, taskId)
.orderByDesc(TaskDetailDO::getId));
}
public List<TaskDetailSumResult> selectSumListByTaskId(Long taskId);
}

@ -5,6 +5,7 @@ import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO;
import org.apache.ibatis.annotations.Mapper;
@ -29,7 +30,26 @@ public interface TaskMapper extends BaseMapperX<TaskDO> {
.betweenIfPresent(TaskDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(TaskDO::getId));
}
default PageResult<TaskDO> selectPage2(TaskPageReqVO reqVO) {
List<Integer> statusList = new ArrayList<>();
statusList.add(TaskStatusEnum..getValue());
statusList.add(TaskStatusEnum..getValue());
statusList.add(TaskStatusEnum..getValue());
return selectPage(reqVO, new LambdaQueryWrapperX<TaskDO>()
.eqIfPresent(TaskDO::getCode, reqVO.getCode())
.betweenIfPresent(TaskDO::getOrderDate, reqVO.getOrderDate())
.betweenIfPresent(TaskDO::getDeliveryDate, reqVO.getDeliveryDate())
.inIfPresent(TaskDO::getStatus,statusList)
.eqIfPresent(TaskDO::getProcessInstanceId, reqVO.getProcessInstanceId())
.eqIfPresent(TaskDO::getRemark, reqVO.getRemark())
.betweenIfPresent(TaskDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(TaskDO::getId));
}
default TaskDO selectByNo(String no) {
return selectOne(TaskDO::getCode, no);
}
default List<TaskDO> selectList(List<Integer> status) {
return selectList(TaskDO::getStatus, status);
}
}

@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.mes.dal.mysql.task;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailSumResult;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.ViewTaskProductSummary;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* Mapper
*
* @author
*/
@Mapper
public interface ViewTaskProductSummaryMapper extends BaseMapperX<ViewTaskProductSummary> {
default List<ViewTaskProductSummary> selectListByTaskId(Long taskId) {
return selectList(ViewTaskProductSummary::getTaskId, taskId);
}
default int deleteByTaskId(Long taskId) {
return delete(ViewTaskProductSummary::getTaskId, taskId);
}
default PageResult<ViewTaskProductSummary> selectPage(PageParam reqVO, Long taskId) {
return selectPage(reqVO, new LambdaQueryWrapperX<ViewTaskProductSummary>()
.eq(ViewTaskProductSummary::getTaskId, taskId)
.orderByDesc(ViewTaskProductSummary::getTaskId));
}
public List<TaskDetailSumResult> selectSumListByTaskId(Long taskId);
}

@ -5,10 +5,13 @@ import java.util.*;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.ViewTaskProductSummary;
import cn.iocoder.yudao.module.mes.service.plan.PlanServiceImpl;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import javax.validation.Valid;
@ -70,9 +73,15 @@ public interface TaskService {
* @return
*/
PageResult<TaskDO> getTaskPage(TaskPageReqVO pageReqVO);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<TaskDO> getTaskPage2(TaskPageReqVO pageReqVO);
// ==================== 子表(生产任务单明细) ====================
List<TaskDO> selectList(List<Integer> status);
/**
*
*
@ -89,7 +98,7 @@ public interface TaskService {
* @return
*/
PageResult<TaskDetailRespVO> getTaskDetailPage(PageParam pageReqVO, Long taskId);
List<ViewTaskProductSummary> getTaskProductSummaryList(Long taskId);
/**
*
*
@ -119,5 +128,12 @@ public interface TaskService {
* @return
*/
TaskDetailDO getTaskDetail(Long id);
/**
*
* @param taskId
* @param sizeOfPlan
* @param productsOfPlan
* @return
*/
List<PlanDO> generatePlan(Long taskId, int sizeOfPlan, int productsOfPlan);
}

@ -6,7 +6,13 @@ import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService;
import cn.iocoder.yudao.module.mes.dal.dataobject.plan.PlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailSumResult;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.ViewTaskProductSummary;
import cn.iocoder.yudao.module.mes.dal.mysql.plan.PlanMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.task.ViewTaskProductSummaryMapper;
import cn.iocoder.yudao.module.mes.dal.redis.no.MesNoRedisDAO;
import cn.iocoder.yudao.module.mes.service.plan.PlanService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@ -113,9 +119,15 @@ public class TaskServiceImpl implements TaskService {
public PageResult<TaskDO> getTaskPage(TaskPageReqVO pageReqVO) {
return taskMapper.selectPage(pageReqVO);
}
@Override
public PageResult<TaskDO> getTaskPage2(TaskPageReqVO pageReqVO) {
return taskMapper.selectPage2(pageReqVO);
}
// ==================== 子表(生产任务单明细) ====================
@Override
public List<TaskDO> selectList(List<Integer> status){
return taskMapper.selectList(status);
}
@Override
public List<TaskDetailDO> getTaskDetailListByTaskId(Long taskId) {
return taskDetailMapper.selectListByTaskId(taskId);
@ -141,11 +153,26 @@ public class TaskServiceImpl implements TaskService {
private ErpProductService productService;
@Resource
private ErpProductUnitService productUnitService;
@Resource
private ViewTaskProductSummaryMapper summaryMapper;
@Resource
private PlanMapper planMapper;
@Override
public PageResult<TaskDetailRespVO> getTaskDetailPage(PageParam pageReqVO, Long taskId) {
PageResult<TaskDetailDO> pageResult = taskDetailMapper.selectPage(pageReqVO, taskId);
return new PageResult<>(buildDetailVOList(pageResult.getList()), pageResult.getTotal());
}
@Override
public List<ViewTaskProductSummary> getTaskProductSummaryList(Long taskId) {
// List<TaskDetailSumResult> resultList = taskDetailMapper.selectSumListByTaskId(taskId);
// System.out.println(Arrays.toString(resultList.toArray()));
List<ViewTaskProductSummary> summaryList = summaryMapper.selectListByTaskId(taskId);
for (ViewTaskProductSummary summary:summaryList) {
Long planTotalNumber = planMapper.selectSum(summary.getTaskId(),summary.getProductId(),null);
summary.setPlanNumber(planTotalNumber);
}
return summaryList;
}
private List<TaskDetailRespVO> buildDetailVOList(List<TaskDetailDO> list) {
if (CollUtil.isEmpty(list)) {
return Collections.emptyList();
@ -196,5 +223,10 @@ public class TaskServiceImpl implements TaskService {
}
@Override
public List<PlanDO> generatePlan(Long taskId, int sizeOfPlan, int productsOfPlan) {
return null;
}
}

@ -0,0 +1,55 @@
<?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="cn.iocoder.yudao.module.mes.dal.mysql.task.TaskDetailMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
<resultMap id="BaseResultMap" type="cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailDO">
<id column="id" property="id" />
<result column="product_id" property="productId" />
<result column="unit_id" property="unitId" />
<result column="task_id" property="taskId" />
<result column="number" property="number" />
<result column="package_size" property="packageSize" />
<result column="project_name" property="projectName" />
<result column="tech_requirements" property="techRequirements" />
<result column="remark" property="remark" />
<result column="is_enable" property="isEnable" />
<result column="creator" property="creator" />
<result column="create_time" property="createTime" />
<result column="updater" property="updater" />
<result column="update_time" property="updateTime" />
<result column="deleted" property="deleted" />
<result column="tenant_id" property="tenantId" />
<result column="package_number" property="packageNumber" />
<result column="finish_date" property="finishDate" />
<result column="boxing_date" property="boxingDate" />
<result column="arrive_date" property="arriveDate" />
<result column="bar_code" property="barCode" />
<result column="attachment" property="attachment" />
<result column="sale_order_code" property="saleOrderCode" />
</resultMap>
<resultMap id="sumResultMap" type="cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailSumResult">
<result column="product_id" property="productId" />
<result column="task_id" property="taskId" />
<result column="number" property="number" />
</resultMap>
<sql id="Base_Column_List">
id, product_id, unit_id, task_id, `number`, package_size, project_name,
tech_requirements, remark, is_enable, creator, create_time, updater, update_time,
deleted, tenant_id, package_number, finish_date, boxing_date, arrive_date,
bar_code, attachment, sale_order_code
</sql>
<!-- 根据ID查询 -->
<select id="selectSumListByTaskId" parameterType="long" resultMap="sumResultMap">
SELECT product_id, task_id, SUM(number) AS number
FROM mes_task_detail
WHERE task_id = #{taskId}
GROUP BY product_id;
</select>
</mapper>

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.service.bom;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@ -32,7 +33,7 @@ import static org.mockito.Mockito.*;
* @author
*/
@Import(BomServiceImpl.class)
public class BomServiceImplTest extends BaseDbUnitTest {
public class BomServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource
private BomServiceImpl bomService;

@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.mes.service.task;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.task.vo.TaskSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.task.TaskDetailSumResult;
import cn.iocoder.yudao.module.mes.dal.mysql.task.TaskDetailMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.task.TaskMapper;
import cn.iocoder.yudao.module.mes.service.machine.MachineComponentServiceImpl;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.TASK_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link TaskServiceImpl}
*
* @author
*/
@Import(MachineComponentServiceImpl.class)
public class MapperTest extends BaseDbUnitTest {
@Resource
private TaskMapper taskMapper;
@Resource
private TaskDetailMapper taskDetailMapper;
@Test
public void testCreateTask_success() {
List<TaskDetailSumResult> list = taskDetailMapper.selectSumListByTaskId(94L);
// 断言
assertNotNull(list);
// 校验记录的属性是否正确
System.out.println(list.size());
}
}

@ -238,6 +238,7 @@ yudao:
- tmp_report_data_income
- iot_gateway
- iot_mqtt_record
- mes_view_task_product_summary
sms-code: # 短信验证码相关的配置项
expire-times: 10m
send-frequency: 1m

Loading…
Cancel
Save