Merge remote-tracking branch 'origin/dev' into dev

yun-zuoyi
汪云昊 5 years ago
commit e8ea5a3c90

@ -2564,20 +2564,17 @@ public class MesEnumUtil {
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MES_QC_CHECK_TYPE {
IN_MATERIAL_CHECK(5, "IN_MATERIAL_CHECK", "来料检验"),
FIRST_CHECK(10, "FIRST_CHECK", "首检"),
ON_SITE_CHECK(20, "ON_SITE_CHECK", "巡检"),
END_CHECK(30, "END_CHECK", "尾检"),
EDELIVERY_CHECK(40, "EDELIVERY_CHECK", "发货");
INCOMING_CHECK(5, "来料检验"),
FIRST_CHECK(10, "首检"),
ON_SITE_CHECK(20, "巡检"),
ND_CHECK(30, "末检"),
RC_CHECK(40, "发货检验");
private int value;
private String code;
private String description;
MES_QC_CHECK_TYPE(int value, String code, String description) {
MES_QC_CHECK_TYPE(int value, String description) {
this.value = value;
this.code = code;
this.description = description;
}
@ -2585,8 +2582,6 @@ public class MesEnumUtil {
return value;
}
public String getCode() {return code;}
public String getDescription() {
return description;
}
@ -5564,6 +5559,78 @@ public class MesEnumUtil {
}
/**
*
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum QC_STATUS {
CREATE(10, "创建"),
RECEIPT(20, "执行中"),
SUBMISSION(30, "提交"),
COMPLETE(40, "完成");
private int value;
private String description;
QC_STATUS(int value, String description) {
this.value = value;
this.description = description;
}
public int getValue() {
return value;
}
public String getDescription() {
return description;
}
public static String valueOfDescription(int val) {
String tmp = null;
for (int i = 0; i < values().length; i++) {
if (values()[i].value == val) {
tmp = values()[i].description;
}
}
return tmp;
}
}
/**
*
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum IN_READ_FLAG {
NOTREAD(0, "未读取"),
READ(1, "已读取");
private int value;
private String description;
IN_READ_FLAG(int value, String description) {
this.value = value;
this.description = description;
}
public int getValue() {
return value;
}
public String getDescription() {
return description;
}
public static String valueOfDescription(int val) {
String tmp = null;
for (int i = 0; i < values().length; i++) {
if (values()[i].value == val) {
tmp = values()[i].description;
}
}
return tmp;
}
}
/**
* MES_
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)

@ -925,10 +925,11 @@ public class MesPcnEnumUtil {
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MES_QC_CHECK_TYPE {
INCOMING_CHECK(5, "来料检验"),
FIRST_CHECK(10, "首检"),
ON_SITE_CHECK(20, "巡检"),
END_CHECK(30, "尾检");
END_CHECK(30, "末检"),
RC_CHECK(40, "发货检验");
private int value;
private String description;
@ -3564,4 +3565,78 @@ public class MesPcnEnumUtil {
return description;
}
}
/**
*
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum QC_STATUS {
CREATE(10, "创建"),
RECEIPT(20, "执行中"),
SUBMISSION(30, "提交"),
COMPLETE(40, "完成");
private int value;
private String description;
QC_STATUS(int value, String description) {
this.value = value;
this.description = description;
}
public int getValue() {
return value;
}
public String getDescription() {
return description;
}
public static String valueOfDescription(int val) {
String tmp = null;
for (int i = 0; i < values().length; i++) {
if (values()[i].value == val) {
tmp = values()[i].description;
}
}
return tmp;
}
}
/**
*
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum CHECK_REULST {
PASS(10, "通过"),
NON_PASS(20, "不通过"),
CONCESSION_PRODUCTION(30, "让步生产");
private int value;
private String description;
CHECK_REULST(int value, String description) {
this.value = value;
this.description = description;
}
public int getValue() {
return value;
}
public String getDescription() {
return description;
}
public static String valueOfDescription(int val) {
String tmp = null;
for (int i = 0; i < values().length; i++) {
if (values()[i].value == val) {
tmp = values()[i].description;
}
}
return tmp;
}
}
}

@ -0,0 +1,89 @@
package cn.estsh.i3plus.pojo.mes.bean;
import cn.estsh.i3plus.pojo.base.bean.BaseBean;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;
/**
* @Description:
* @Reference:
* @Author: jessica.chen
* @CreateDate: 2019\11\15 10:01
* @Modify:
**/
@Data
@Entity
@DynamicInsert
@DynamicUpdate
@EqualsAndHashCode(callSuper = true)
@Table(name = "IF_QC_ORDER")
@Api("IF_质检单")
public class IfQcOrder extends BaseBean implements Serializable {
private static final long serialVersionUID = -5412635747424111476L;
@Column(name = "ORDER_NO")
@ApiParam("质检单号")
private String orderNo;
@Column(name = "PART_NO")
@ApiParam("物料号")
private String partNo;
@Column(name = "QTY")
@ApiParam("数量")
private Double qty = 0D;
@Column(name = "UNIT")
@ApiParam("单位")
private String unit;
@Column(name = "SUPPLIER_CODE")
@ApiParam("供应商代码")
private String supplierCode;
@Column(name = "SRC_LOT_NO")
@ApiParam("收货批次")
private String srcLotNo;
@Column(name = "FIX_LOT_NO")
@ApiParam("特殊批次")
private String fixLotNo;
@Column(name = "RELATION_ORDER_NO")
@ApiParam("关联单号")
private String relationOrderNo;
@Column(name = "UUID")
@ApiParam("数据唯一编号")
private String uuid;
@Column(name = "ORGANIZTION_CODE")
@ApiParam("工厂代码")
private String organiztionCode;
@Column(name = "SYNC_STATUS")
@ApiParam("同步状态")
private Integer syncStatus;
@Column(name = "ERROR_MESSAGE")
@ApiParam("异常消息")
private String errorMessage;
@Column(name = "ACTION_CODE")
@ApiParam("动作代码")
private String actionCode;
@Column(name = "IF_CODE")
@ApiParam("接口代码")
private String ifCode;
}

@ -0,0 +1,64 @@
package cn.estsh.i3plus.pojo.mes.bean;
import cn.estsh.i3plus.pojo.base.bean.BaseBean;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;
/**
* @Description:
* @Reference:
* @Author: jessica.chen
* @CreateDate: 2019\11\15 10:01
* @Modify:
**/
@Data
@Entity
@DynamicInsert
@DynamicUpdate
@EqualsAndHashCode(callSuper = true)
@Table(name = "IF_QC_RESULT")
@Api("IF_质检结果")
public class IfQcResult extends BaseBean implements Serializable {
private static final long serialVersionUID = -5412635747424111476L;
@Column(name = "ORDER_NO")
@ApiParam("质检单号")
private String orderNo;
@Column(name = "UUID")
@ApiParam("数据唯一编号")
private String uuid;
@Column(name = "QC_RESULT")
@ApiParam("质检结果")
private String qcResult;
@Column(name = "ORGANIZTION_CODE")
@ApiParam("工厂代码")
private String organiztionCode;
@Column(name = "SYNC_STATUS")
@ApiParam("同步状态")
private Integer syncStatus;
@Column(name = "ERROR_MESSAGE")
@ApiParam("异常消息")
private String errorMessage;
@Column(name = "ACTION_CODE")
@ApiParam("动作代码")
private String actionCode;
@Column(name = "IF_CODE")
@ApiParam("接口代码")
private String ifCode;
}

@ -0,0 +1,60 @@
package cn.estsh.i3plus.pojo.mes.bean;
import cn.estsh.i3plus.pojo.base.bean.BaseBean;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;
/**
* @Description:
* @Author: jokelin
* @Date: 2020/5/12 5:53
* @Modify:
*/
@Data
@Entity
@DynamicInsert
@DynamicUpdate
@EqualsAndHashCode(callSuper = true)
@Table(name = "MES_PART_STOCK_SCHEDULE")
@Api("MES_库存排程配置")
public class MesPartStockSchedule extends BaseBean implements Serializable {
private static final long serialVersionUID = -8265586624645977526L;
@Column(name = "PART_NO")
@ApiParam("物料号")
private String partNo;
@Column(name = "MIN_CAPACITY")
@ApiParam("最小库存")
private Integer minCapacity;
@Column(name = "TOTAL_CAPACITY")
@ApiParam("总容量")
private Integer totalCapacity;
@Column(name = "STANDARD_CAPACITY")
@ApiParam("标准值")
private Integer standardCapacity;
@Column(name = "SCHEDULE_CAPACITY")
@ApiParam("待生产箱数")
private Integer scheduleCapacity;
@Column(name = "PLAN_QTY")
@ApiParam("产品JSP个数")
private Integer planQty;
@Column(name = "TOTAL_PLAN_QTY")
@ApiParam("JSP总数")
private Integer totalPlanQty;
}

@ -130,6 +130,10 @@ public class MesPlc extends BaseBean implements Serializable {
@ApiParam("密码")
private String password;
@Column(name = "DEFAULT_RULE")
@ApiParam("默认赋值属性")
private String defaultRule;
@Transient
@ApiParam("设备名称")
private String equipmentName;

@ -14,13 +14,12 @@ import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @Description :mes
* @Reference :
* @Author : jack.jia
* @CreateDate : 2019-04-12
* @Description:
* @Reference:
* @Author: jessica.chen
* @CreateDate: 2019\11\15 10:01
* @Modify:
**/
@Data
@ -30,8 +29,9 @@ import java.math.BigDecimal;
@EqualsAndHashCode(callSuper = true)
@Table(name = "MES_QC_ORDER")
@Api("MES_检验单")
public class MesQcOrder extends BaseBean implements Serializable {
private static final long serialVersionUID = -8961182786427690154L;
public class MesQcOrder extends BaseBean implements Serializable {
private static final long serialVersionUID = -5412635747424111476L;
@Column(name = "ORDER_NO")
@ApiParam("质检单号")
private String orderNo;
@ -48,9 +48,13 @@ public class MesQcOrder extends BaseBean implements Serializable {
@ApiParam("物料名称")
private String partName;
@Column(name = "FINISH_QTY")
@ApiParam("已检数量")
private Double finishQty = 0D;
@Column(name = "QTY")
@ApiParam("数量")
private BigDecimal qty;
private Double qty = 0D;
@Column(name = "UNIT")
@ApiParam("单位")
@ -74,17 +78,34 @@ public class MesQcOrder extends BaseBean implements Serializable {
@Column(name = "UUID")
@ApiParam("数据唯一编号")
private String uuId;
private String uuid;
@Column(name = "STATUS")
@ApiParam("状态")
private Integer status;
@Column(name = "CHECK_RESULT")
@Column(name = "CHECK_REULST")
@ApiParam("检测整体结果")
private Integer checkResult;
private Integer checkReulst = 0;
@Column(name = "MEMO")
@ApiParam("备注")
private String memo;
@ApiParam(value = "检测结果")
@Transient
private String reulstStatus;
@ApiParam(value = "产品条码")
@Transient
@ApiParam("供应商名称")
private String supplierName;
private String serialNumber;
@ApiParam(value = "产线")
@Transient
private String workCenterCode;
@ApiParam(value = "工单号")
@Transient
private String mesWorkNo;
}

@ -16,10 +16,10 @@ import javax.persistence.Transient;
import java.io.Serializable;
/**
* @Description :mes
* @Reference :
* @Author : jack.jia
* @CreateDate : 2019-04-12
* @Description:
* @Reference:
* @Author: jessica.chen
* @CreateDate: 2019\11\15 10:01
* @Modify:
**/
@Data
@ -29,8 +29,9 @@ import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Table(name = "MES_QC_ORDER_DETAIL")
@Api("MES_检验单明细")
public class MesQcOrderDetail extends BaseBean implements Serializable {
private static final long serialVersionUID = -8961182786786500154L;
public class MesQcOrderDetail extends BaseBean implements Serializable {
private static final long serialVersionUID = -5412635747424111476L;
@Column(name = "ORDER_NO")
@ApiParam("质检单号")
private String orderNo;
@ -61,25 +62,25 @@ public class MesQcOrderDetail extends BaseBean implements Serializable {
@Column(name = "IS_CONCESSION")
@ApiParam("是否允许让步")
private String isConcession;
private Integer isConcession = 0;
@Column(name = "CHECK_REULST")
@ApiParam("判定结果")
private Integer checkReulst = 0;
@Column(name = "STATUS")
@ApiParam("状态")
private Integer status;
@Column(name = "ITEM_NO")
@ApiParam("检验行号")
private Integer itemNo = 0;
@Column(name = "CHECK_RESULT")
@ApiParam("检测整体结果")
private Integer checkResult;
@Column(name = "SERIAL_NUMBER")
@ApiParam("产品条码")
private String serialNumber;
@Column(name = "CHECK_VALUE")
@ApiParam("检测值")
private String checkValue;
@ApiParam(value = "已完成数量")
@Transient
@ApiParam("本批次生产总数")
private Integer currentLotNoSum;
@Transient
@ApiParam("收货批次")
private String srcLotNo;
private Integer finishQty;
}

@ -0,0 +1,94 @@
package cn.estsh.i3plus.pojo.mes.model;
import cn.estsh.i3plus.pojo.base.bean.BaseBean;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description:
* @Reference:
* @Author: jessica.chen
* @CreateDate: 2019\11\18 9:34
* @Modify:
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Api("QC质检单model")
public class IfQcOrderDataModel extends BaseBean {
@ApiParam("ruid")
private Long ruid;
@ApiParam("收货流水号")
private Long uuid;
@ApiParam("域")
private String werks;
@ApiParam("地点")
private String lgort;
@ApiParam("收货库位")
private String wlbrt;
@ApiParam("单据号")
private String refid;
@ApiParam("行号")
private Integer zeile;
@ApiParam("业务类型")
private String action;
@ApiParam("供应商编码")
private String lifnr;
@ApiParam("物料编码")
private String matnr;
@ApiParam("收货数量")
private String erfmg;
@ApiParam("计量单位")
private String erfme;
@ApiParam("检验编号")
private String chkno;
@ApiParam("炉批号")
private String stono;
@ApiParam("收货日期")
private String zpost;
@ApiParam("收货时间")
private String ztime;
@ApiParam("添加日期")
private String addtimeYmd;
@ApiParam("添加时间")
private String addtimeHms;
@ApiParam("处理日期")
private String finishtimeYmd;
@ApiParam("处理时间")
private String finishtimeHms;
@ApiParam("删除标记")
private Integer deleteFlag;
@ApiParam("读取标识")
private Integer readFlag;
@ApiParam("错误代码")
private String errCode;
@ApiParam("错误信息")
private String errMsg;
}

@ -0,0 +1,15 @@
package cn.estsh.i3plus.pojo.mes.repository;
import cn.estsh.i3plus.pojo.base.jpa.dao.BaseRepository;
import cn.estsh.i3plus.pojo.mes.bean.IfPackageDetail;
import cn.estsh.i3plus.pojo.mes.bean.IfQcOrder;
/**
* @Description:
* @Reference:
* @Author: joke.wang
* @CreateDate: 2019\11\18 10:34
* @Modify:
**/
public interface IfQcOrderRepository extends BaseRepository<IfQcOrder, Long> {
}

@ -0,0 +1,14 @@
package cn.estsh.i3plus.pojo.mes.repository;
import cn.estsh.i3plus.pojo.base.jpa.dao.BaseRepository;
import cn.estsh.i3plus.pojo.mes.bean.IfQcResult;
/**
* @Description:
* @Reference:
* @Author: joke.wang
* @CreateDate: 2019\11\18 10:34
* @Modify:
**/
public interface IfQcResultRepository extends BaseRepository<IfQcResult, Long> {
}

@ -0,0 +1,15 @@
package cn.estsh.i3plus.pojo.mes.repository;
import cn.estsh.i3plus.pojo.base.jpa.dao.BaseRepository;
import cn.estsh.i3plus.pojo.mes.bean.MesPartStockSchedule;
import org.springframework.stereotype.Repository;
/**
* @Description:
* @Author: jokelin
* @Date: 2020/5/12 5:57
* @Modify:
*/
@Repository
public interface MesPartStockScheduleRepository extends BaseRepository<MesPartStockSchedule, Long> {
}

@ -3,6 +3,7 @@ package cn.estsh.i3plus.pojo.mes.sqlpack;
import cn.estsh.i3plus.pojo.base.bean.BaseBean;
import cn.estsh.i3plus.pojo.base.bean.DdlPackBean;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.ImppEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.base.tool.DdlPreparedPack;
import cn.estsh.i3plus.pojo.mes.bean.*;
@ -437,4 +438,22 @@ public class MesPcnHqlPack {
return packBean;
}
/**
*
*
* @param mesQcOrderDetail
* @param organizeCode
* @return
*/
public static DdlPackBean getMesQcOrderDetail(MesQcOrderDetail mesQcOrderDetail, String organizeCode) {
DdlPackBean packBean = getAllBaseData(organizeCode);
DdlPreparedPack.getStringEqualPack(mesQcOrderDetail.getOrderNo(), "orderNo", packBean);
DdlPreparedPack.getNumEqualPack(mesQcOrderDetail.getCheckType(), "checkType", packBean);
DdlPreparedPack.getStringEqualPack(mesQcOrderDetail.getCheckItemType(), "checkItemType", packBean);
if(mesQcOrderDetail.getCheckType() == MesPcnEnumUtil.MES_QC_CHECK_TYPE.FIRST_CHECK.getValue()){
DdlPreparedPack.getNumEqualPack(mesQcOrderDetail.getFinishQty()+1, "itemNo", packBean);
}
return packBean;
}
}

@ -73,6 +73,35 @@ public class PrintOrderDataModel implements Serializable {
@Transient
public String containerNo;
@ApiParam("道口")
@Transient
public String dock;
@ApiParam("泊位")
@Transient
public String zdoc;
@Transient
@ApiParam("计划交货日期")
private String planDate;
@Transient
@ApiParam("计划交货时间")
private String planTime;
@Transient
@ApiParam(value = "创建日期查询用,查询起始日期", example = "2018-01-01 01:00:00")
@AnnoOutputColumn(hidden = true)
public String createDateTimeStart;
@Transient
@ApiParam(value = "创建日期查询用,查询结束日期", example = "2018-12-31 23:59:59")
@AnnoOutputColumn(hidden = true)
public String createDateTimeEnd;
@Transient
public String createDateTime;
public int getBusiDataType() {
if (busiDataType != null) {
return busiDataType.intValue();

@ -10,7 +10,11 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.List;
/**
@ -115,10 +119,14 @@ public class WmsASNMaster extends BaseBean {
@ApiParam("备注")
public String remark;
@Column(name = "DOCK",columnDefinition = "varchar(50) default '1'")
@Column(name = "DOCK", columnDefinition = "varchar(50) default ''")
@ApiParam("道口")
public String dock;
@ApiParam("泊位")
@Column(name = "ZDOC", columnDefinition = "varchar(50) default ''")
public String zdoc;
@Transient
@ApiParam("供应商电话")
private String vendorCall;

@ -10,7 +10,11 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.List;
/**
@ -159,10 +163,14 @@ public class WmsPOMaster extends BaseBean {
@Transient
private List<String> orderNoList;
@Column(name = "DOCK",columnDefinition = "varchar(50) default '1'")
@Column(name = "DOCK", columnDefinition = "varchar(50) default ''")
@ApiParam("道口")
public String dock;
@ApiParam("泊位")
@Column(name = "ZDOC", columnDefinition = "varchar(50) default ''")
public String zdoc;
public int getPoStatusVal() {
return this.poStatus == null ? 0 : this.poStatus;
}

@ -212,6 +212,17 @@ public class WmsStockSn extends BaseBean {
@DynamicField(webFieldType = CommonEnumUtil.FIELD_TYPE.SELECT, dataSrc = "WMS_STOCK_TYPE")
private Integer snType = 10;
@ApiParam(value = "领用次数", example = "0")
@ColumnDefault("0")
@DynamicField(webFieldType = CommonEnumUtil.FIELD_TYPE.NUMBER, isRequire = 2)
private Integer useCount;
@Column(name = "SEQ_NO")
@ApiParam(value = "序号", example = "0")
@ColumnDefault("0")
@DynamicField(webFieldType = CommonEnumUtil.FIELD_TYPE.NUMBER, isRequire = 2)
private Integer seqNo;
@ApiParam(value = "仓库名称")
@Transient
public String whNameRdd;

@ -39,6 +39,10 @@ public class WmsThymeleaf extends BaseBean {
@ApiParam("模板描述")
private String tlDesc;
@Column(name = "PROMPT_MSG")
@ApiParam("引导消息")
private String promptMsg;
@Lob
@Column(name = "TL_CONTENT", columnDefinition = "TEXT")
@ApiParam(value = "模板内容")

@ -279,6 +279,12 @@ public class WmsHqlPack {
DdlPreparedPack.getInPackList(wmsPOMaster.getOrderNoList(), "orderNo", result);
}
//按创建时间查询单据信息
if (StringUtils.isNotBlank(wmsPOMaster.getCreateDateTimeStart())) {
DdlPreparedPack.timeBuilder(wmsPOMaster.getCreateDateTimeStart(),
wmsPOMaster.getCreateDateTimeEnd(), "createDatetime", result, true);
}
getStringBuilderPack(wmsPOMaster, result);
return result;
@ -693,6 +699,12 @@ public class WmsHqlPack {
DdlPreparedPack.getInPackList(wmsASNMaster.getOrderNoList(), "orderNo", result);
}
//按创建时间查询单据信息
if (StringUtils.isNotBlank(wmsASNMaster.getCreateDateTimeStart())) {
DdlPreparedPack.timeBuilder(wmsASNMaster.getCreateDateTimeStart(),
wmsASNMaster.getCreateDateTimeEnd(), "createDatetime", result, true);
}
getStringBuilderPack(wmsASNMaster, result);
return result;

Loading…
Cancel
Save