feat: 시민신고 상세 삭제

파일 상세 삭제 및 파일 삭제
main
minuk926 2 years ago
parent 9db51e86d5
commit 93e61bbac2

@ -18,6 +18,8 @@ public interface IEcCtznSttemntMapper {
int insertEcCtznSttemnt(final CtznStmtDTO dto);
int insertEcCtznSttemntDetail(final CtznStmtDTO.CtznStmtDtl dtl);
int updateEcCtznSttemntDetail(final CtznStmtDTO.CtznStmtDtl dtl);
int deleteEcCtznSttemntDetail(final CtznStmtDTO.CtznStmtDtl dtl);
List<CtznStmtDTO> selectCtznSttemnts(final Map<String, Object> paraMap, final RowBounds rowBounds);

@ -207,6 +207,8 @@ public class CtznStmtDTO implements Serializable { //extends ExtlEsbDataType {
private String updusr;
private Set<Integer> indexs = new HashSet<>();
private boolean removeImageData = false;
}
@Getter

@ -8,8 +8,10 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import kr.xit.fims.biz.FimsConst;
import kr.xit.fims.biz.ec.mapper.IEcCtznSttemntMapper;
import kr.xit.fims.biz.ec.model.CtznStmtDTO;
import kr.xit.framework.biz.cmm.model.CmmFileDTO;
import kr.xit.framework.biz.cmm.service.ICmmFileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -53,4 +55,26 @@ public class EcCtznSttemntService implements IEcCtznSttemntService {
return mapper.selectEcCtznSttemntDetail(dto);
}
@Override
@Transactional
public void modifyEcCtznSttemntDetail(final CtznStmtDTO.CtznStmtDtl dto) {
dto.setUpdusr(getUserUniqId());
mapper.updateEcCtznSttemntDetail(dto);
}
@Override
@Transactional
public void removeEcCtznSttemntDetail(final CtznStmtDTO.CtznStmtDtl dto) {
mapper.deleteEcCtznSttemntDetail(dto);
// 첨부파일 삭제
if(dto.isRemoveImageData()){
CmmFileDTO.PaintwebReq pwDTO = CmmFileDTO.PaintwebReq.builder()
.jobSeCode(FimsConst.FileJobSeCode.NATL_NEWS_PAPER_RCV.getCode())
.fileJobId(dto.getInterfaceSeqN()+dto.getCtznSttemntDetailSn())
.build();
cmmFileService.removeAllCtznStmtFiles(pwDTO);
}
}
}

@ -440,17 +440,17 @@ public class EcNatlNewspaperService implements IEcNatlNewspaperService {
method = clz.getMethod("getApndfilcont" + fileIdx, null);
String petiFileStr = (String)method.invoke(apndFileDTO, null);
//TODO: 실제 저장할 파일명 생성후 파일 내용 저장
// TODO: 실제 저장할 파일명 생성후 파일 내용 저장
//String saveFileName = CommUtils.getStringFromUUID();
//CommUtils.saveFileFromBytes(saveFileName, makePath, Base64.decode(petiFileStr.getBytes()));
// 실제 저장된 파일명 set
//method = clz.getMethod("setPetiFileName" + (idx + 1), String.class);
//method.invoke(appendFileDto, saveFileName);
//method = clz.getMethod("setPetiFileName" + fileIdx, String.class);
//method.invoke(apndFileDTO, petiFileNm);
//TODO: 원본 파일명으로 파일 생성후 파일 업로드
// TODO: 원본 파일명으로 파일 생성후 파일 업로드
mfList.add(CommUtils.createMutipartFileFromBytes(petiFileNm, makePath, Base64.decode(petiFileStr.getBytes())));
//method.invoke(dto, StringUtils.cleanPath(makePath + mf.getOriginalFilename()));
//method.invoke(apndFileDTO, StringUtils.cleanPath(makePath + mf.getOriginalFilename()));
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.getStackTrace();
throw BizRuntimeException.create(MessageKey.CUSTOM_MSG, e.getMessage());

@ -4,7 +4,6 @@ import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.RowBounds;
import org.apache.poi.ss.formula.functions.T;
import kr.xit.fims.biz.ec.model.CtznStmtDTO;
import kr.xit.framework.core.utils.XitCmmnUtil;
@ -15,7 +14,8 @@ public interface IEcCtznSttemntService {
List<CtznStmtDTO.CtznStmtDtl> findCtznStmtDtls(final CtznStmtDTO.Request reqDTO);
CtznStmtDTO.CtznStmtDtl findCtznStmtDtl(final CtznStmtDTO.Request reqDTO);
void modifyEcCtznSttemntDetail(final CtznStmtDTO.CtznStmtDtl dto);
void removeEcCtznSttemntDetail(final CtznStmtDTO.CtznStmtDtl dto);
default String getUserUniqId(){
return XitCmmnUtil.getUserUniqId();

@ -7,6 +7,7 @@ import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@ -16,8 +17,11 @@ import kr.xit.fims.biz.ec.model.CtznStmtDTO;
import kr.xit.fims.biz.ec.service.IEcCtznSttemntService;
import kr.xit.framework.biz.cmm.model.CmmFileDTO;
import kr.xit.framework.biz.cmm.service.ICmmFileService;
import kr.xit.framework.core.constants.FrameworkConstants;
import kr.xit.framework.core.model.ResultResponse;
import kr.xit.framework.support.mybatis.MybatisUtils;
import kr.xit.framework.support.util.AjaxMessageMapRenderer;
import kr.xit.framework.support.util.constants.MessageKey;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -81,4 +85,22 @@ public class EcCtznSttemntController {
.collect(Collectors.toList())
);
}
@PostMapping("/modifyCtznStmtDtl")
public ModelAndView modifyCtznStmtDtl(final CtznStmtDTO.CtznStmtDtl dto) {
ModelAndView mav = new ModelAndView(FrameworkConstants.JSON_VIEW);
service.modifyEcCtznSttemntDetail(dto);
AjaxMessageMapRenderer.success(mav, MessageKey.CMM_UPDATE_SUCCESS);
return mav;
}
@PostMapping("/removeCtznStmtDtl")
public ModelAndView removeCtznStmtDtl(final CtznStmtDTO.CtznStmtDtl dto) {
ModelAndView mav = new ModelAndView(FrameworkConstants.JSON_VIEW);
service.removeEcCtznSttemntDetail(dto);
AjaxMessageMapRenderer.success(mav, MessageKey.CMM_DELETE_FAIL);
return mav;
}
}

@ -13,32 +13,33 @@ public interface ICmmFileMapper {
// CmmFileMaster
//-------------------------------------------------------------
Optional<CmmFileDTO.FileMst> selectCmmFileMastr(final String fileMstId);
<T> Optional<String> selectFileMastrIdByJobSeCodeAndJobId(final T t);
int insertCmmFileMastr(final CmmFileDTO.FileMst cmmFileMst);
int deleteCmmFileMastr(final String fileMastrId);
//-------------------------------------------------------------
// CmmFileDtl
//-------------------------------------------------------------
List<CmmFileDTO.FileDtl> selectFilesByFileMastrId(final String fileMastrId);
List<CmmFileDTO.FileDtl> selectCmmFileDetails(final String fileMstId);
int insertCmmFileDetail(final CmmFileDTO.FileDtl cmmFileDtl);
Optional<CmmFileDTO.FileDtl> selectCmmFileDetail(final CmmFileDTO.FileDtl dto);
CmmFileDTO.FileDtl selectCmmFileDetail(final CmmFileDTO.FileDtl dto);
/**
* <pre>
* (jobSeCode) (fileJobId)
* ex) jobSeCode - NatlNewspaper-rcv (:)
* fileJobId - 2022091609485096255243399 ( PK)
* @param dto CmmFileMstDTO
* @param t jobSeCode fileJobId Map DTO / VO
* @return List<CmmFileDtlDTO>
* </pre>
*/
List<CmmFileDTO.FileDtl> selectFilesByJobSeCodeAndJobId(final CmmFileDTO.FileMst dto);
<T> List<CmmFileDTO.FileDtl> selectFilesByJobSeCodeAndJobId(final T t);
int deleteCmmFileDetail(final CmmFileDTO.FileDtl cmmFileDtl);

@ -77,49 +77,20 @@ public class CmmFileService implements ICmmFileService {
return fileMstDTO;
}
@Transactional
public void removeExistsUploadFile(final CmmFileDTO.FileDtl dtlDto){
mapper.selectCmmFileDetail(dtlDto)
.ifPresent((dto)->{
mapper.deleteCmmFileDetail(dto);
new File(this.uploadRoot + dto.getFileCours() + "/" + dto.getFileId()).delete();
});
}
/**
*
* + ,
* @param fileMstDTO
* @param files
* @param pngFileDtlDto
* @return
*
* @param dtlDto
*/
@Override
@Transactional
public CmmFileDTO.FileMst saveCtznStmtFiles(final CmmFileDTO.FileMst fileMstDTO, final List<MultipartFile> files, final CmmFileDTO.FileDtl pngFileDtlDto) {
boolean isCheckExists = setCmmFileMst(fileMstDTO);
mapper.insertCmmFileMastr(fileMstDTO);
String makePath = makeUploadFilePath(fileMstDTO);
String fileUploadPath = this.uploadRoot + makePath;
File file = new File(fileUploadPath);
if(!file.exists()) file.mkdirs();
// 단속 이미지 저장
List<CmmFileDTO.FileDtl> cmmFileDtls = saveFileDtls(fileMstDTO, files, isCheckExists, makePath, fileUploadPath);
// 위도경도 png 이미지 저장
pngFileDtlDto.setFileMastrId(fileMstDTO.getFileMastrId());
//pngFileDtlDto.setFileId(CommUtils.getStringFromUUID());
mapper.insertCmmFileDetail(pngFileDtlDto);
cmmFileDtls.add(pngFileDtlDto);
fileMstDTO.getCmmFileDtls().addAll(cmmFileDtls);
return fileMstDTO;
public void removeExistsUploadFile(final CmmFileDTO.FileDtl dtlDto){
CmmFileDTO.FileDtl selectDTO = mapper.selectCmmFileDetail(dtlDto);
if(Checks.isNotEmpty(selectDTO)){
mapper.deleteCmmFileDetail(selectDTO);
new File(this.uploadRoot + selectDTO.getFileCours() + "/" + selectDTO.getFileId()).delete();
};
}
/**
* file
* @param dto
@ -128,6 +99,7 @@ public class CmmFileService implements ICmmFileService {
* @return
*/
@Override
@Transactional
public List<CmmFileDTO.FileDtl> saveAddFileDtl(final CmmFileDTO.FileDtl dto, final List<MultipartFile> files,
boolean isCheckExists) {
List<CmmFileDTO.FileDtl> cmmFileDtls = new ArrayList<>();
@ -196,6 +168,7 @@ public class CmmFileService implements ICmmFileService {
* @param fileUploadPath
* @return
*/
@Transactional
public List<CmmFileDTO.FileDtl> saveFileDtls(CmmFileDTO.FileMst fileMstDTO, List<MultipartFile> files,
boolean isCheckExists, String makePath, String fileUploadPath) {
List<CmmFileDTO.FileDtl> cmmFileDtls = new ArrayList<>();
@ -255,14 +228,15 @@ public class CmmFileService implements ICmmFileService {
return cmmFileDtls;
}
/**
*
* @param dto
* @return
*/
@Override
public List<CmmFileDTO.FileDtl> findFilesByJobSeCodeAndJobId(final CmmFileDTO.FileMst dto) {
return mapper.selectFilesByJobSeCodeAndJobId(dto);
}
@Override
public List<CmmFileDTO.FileDtl> findFilesByEsbInterfaces(final CmmFileDTO.FileMst dto) {
return mapper.selectFilesByEsbInterfaces(dto);
@Transactional(readOnly = true)
public <T> List<CmmFileDTO.FileDtl> findFilesByJobSeCodeAndJobId(final T t) {
return mapper.selectFilesByJobSeCodeAndJobId(t);
}
private static String makeUploadFilePath(CmmFileDTO.FileMst fileMstDTO) {
@ -295,5 +269,81 @@ public class CmmFileService implements ICmmFileService {
return isCheckExists;
}
//-----------------------------------------------------------------------------------------------
// 시민신고 - 국민신문고 업무
//-----------------------------------------------------------------------------------------------
@Override
@Transactional(readOnly = true)
public List<CmmFileDTO.FileDtl> findFilesByEsbInterfaces(final CmmFileDTO.FileMst dto) {
return mapper.selectFilesByEsbInterfaces(dto);
}
/**
*
* + ,
* @param fileMstDTO
* @param files
* @param pngFileDtlDto
* @return
*/
@Override
@Transactional
public CmmFileDTO.FileMst saveCtznStmtFiles(final CmmFileDTO.FileMst fileMstDTO, final List<MultipartFile> files, final CmmFileDTO.FileDtl pngFileDtlDto) {
boolean isCheckExists = setCmmFileMst(fileMstDTO);
mapper.insertCmmFileMastr(fileMstDTO);
String makePath = makeUploadFilePath(fileMstDTO);
String fileUploadPath = this.uploadRoot + makePath;
File file = new File(fileUploadPath);
if(!file.exists()) file.mkdirs();
// 단속 이미지 저장
List<CmmFileDTO.FileDtl> cmmFileDtls = saveFileDtls(fileMstDTO, files, isCheckExists, makePath, fileUploadPath);
// 위도경도 png 이미지 저장
pngFileDtlDto.setFileMastrId(fileMstDTO.getFileMastrId());
//pngFileDtlDto.setFileId(CommUtils.getStringFromUUID());
mapper.insertCmmFileDetail(pngFileDtlDto);
cmmFileDtls.add(pngFileDtlDto);
fileMstDTO.getCmmFileDtls().addAll(cmmFileDtls);
return fileMstDTO;
}
@Override
@Transactional
public <T> void removeAllCtznStmtFiles(T t) {
// 첨부 파일 목록 조회 : 업무코드 와 업무키로
List<CmmFileDTO.FileDtl> dtlList = mapper.selectFilesByJobSeCodeAndJobId(t);
if(dtlList.size() == 0){
throw BizRuntimeException.create(MessageKey.CUSTOM_MSG, "파일정보오류[유효하지 않은 파일 정보]");
}
// 첨부파일 삭제
dtlList.forEach(this::removeExistsUploadFile);
// 파일 마스터 정보 삭제
mapper.deleteCmmFileMastr(dtlList.get(0).getFileMastrId());
}
@Override
@Transactional
public void removeCtznStmtFile(final CmmFileDTO.FileDtl dto) {
// 첨부파일 목록 조회 - 파일 마스터 ID로
List<CmmFileDTO.FileDtl> dtlList = mapper.selectFilesByFileMastrId(dto.getFileMastrId());
if(dtlList.size() == 0){
throw BizRuntimeException.create(MessageKey.CUSTOM_MSG, "파일정보오류[유효하지 않은 파일 정보]");
}
// 해당 파일 삭제
this.removeExistsUploadFile(dto);
// 첨부파일이 1개 였으면 마스터 정보도 삭제
if(dtlList.size() == 1) mapper.deleteCmmFileMastr(dtlList.get(0).getFileMastrId());
}
}

@ -22,11 +22,7 @@ public interface ICmmFileService {
List<CmmFileDTO.FileDtl> saveAddFileDtl(final CmmFileDTO.FileDtl dto, final List<MultipartFile> files,
final boolean isCheckExists);
@SuppressWarnings("UnusedReturnValue")
CmmFileDTO.FileMst saveCtznStmtFiles(final CmmFileDTO.FileMst fileMstDTO, final List<MultipartFile> files, final CmmFileDTO.FileDtl pngFileDtlDto);
List<CmmFileDTO.FileDtl> findFilesByJobSeCodeAndJobId(final CmmFileDTO.FileMst dto);
<T> List<CmmFileDTO.FileDtl> findFilesByJobSeCodeAndJobId(final T t);
//-------------------------------------------------------------
// 시민신고 : NatlNewspaper
@ -34,6 +30,11 @@ public interface ICmmFileService {
List<CmmFileDTO.FileDtl> findFilesByEsbInterfaces(final CmmFileDTO.FileMst fileMstDTO);
@SuppressWarnings("UnusedReturnValue")
CmmFileDTO.FileMst saveCtznStmtFiles(final CmmFileDTO.FileMst fileMstDTO, final List<MultipartFile> files, final CmmFileDTO.FileDtl pngFileDtlDto);
<T> void removeAllCtznStmtFiles(final T t);
void removeCtznStmtFile(final CmmFileDTO.FileDtl dto);

@ -178,6 +178,26 @@
)
</insert>
<update id="updateEcCtznSttemntDetail">
/* ec-ctzn-sttemnt-mysql-mapper|updateEcCtznSttemntDetail-시민신고 상세 변경|julim */
UPDATE tb_ec_ctzn_sttemnt_detail
SET vhcle_no = #{vhcleNo}
, updt_dt = DATE_FORMAT(NOW(), '%Y%m%d%H%i%s')
, updusr = #{updusr}
WHERE interface_seq_n = #{interfaceSeqN}
AND ctzn_sttemnt_detail_sn = #{ctznSttemntDetailSn}
</update>
<delete id="deleteEcCtznSttemntDetail">
/* ec-ctzn-sttemnt-mysql-mapper|deleteEcCtznSttemntDetail-시민신고 상세 삭제|julim */
DELETE
FROM tb_ec_ctzn_sttemnt_detail
WHERE interface_seq_n = #{interfaceSeqN}
AND ctzn_sttemnt_detail_sn = #{ctznSttemntDetailSn}
</delete>
<resultMap id="resultMapCtznStmt" type="kr.xit.fims.biz.ec.model.CtznStmtDTO">
<result column="interface_seq_n" property="interfaceSeqN"/>
<result column="instt_code" property="insttCode"/>

@ -17,6 +17,13 @@
WHERE file_mastr_id = #{fileMastrId}
</select>
<select id="selectFileMastrIdByJobSeCodeAndJobId" resultType="string">
/* cmm-file-mysql-mapper|selectFileMastrIdByJobSeCodeAndJobId- 파일 마스터 아이디 조회|julim */
SELECT file_mastr_id
FROM tb_cmm_file_mastr
WHERE job_se_code = #{jobSeCode}
AND file_job_id = #{fileJobId}
</select>
<insert id="insertCmmFileMastr">
/* cmm-file-mysql-mapper|insertCmmFileMastr- 파일 마스터 정보 등록|julim */
@ -62,8 +69,8 @@
, tcfd.register
</sql>
<select id="select" resultType="kr.xit.framework.biz.cmm.model.CmmFileDTO$FileDtl">
/* cmm-file-mysql-mapper|selectCmmFileDetails- 파일 상세 목록 조회|julim */
<select id="selectFilesByFileMastrId" resultType="kr.xit.framework.biz.cmm.model.CmmFileDTO$FileDtl">
/* cmm-file-mysql-mapper|selectFilesByFileMastrId- 파일 상세 목록 조회-master id로|julim */
<include refid="sqlCmmFileDetail"/>
FROM tb_cmm_file_detail tcfd
WHERE tcfd.file_mastr_id = #{fileMastrId}
@ -118,7 +125,7 @@
)
</insert>
<insert id="deleteCmmFileDetail">
<delete id="deleteCmmFileDetail">
/* cmm-file-mysql-mapper|deleteCmmFileDetail- 파일 상세 정보 삭제|julim */
DELETE
FROM tb_cmm_file_detail
@ -126,7 +133,7 @@
<if test='fileId != null and fileId != ""'>
AND file_id = #{fileId}
</if>
</insert>
</delete>
<!--suppress SqlResolve -->

@ -123,9 +123,14 @@
</table>
</form>
<form name="frmStmtDtl">
<c:forEach var="dtlDTO" items="${ctznStmtDtlDTOs}" varStatus="status">
<c:forEach var="dtlDTO" items="${ctznStmtDtlDTOs}" varStatus="status">
<form name="frmStmtDtl${dtlDTO.ctznSttemntDetailSn}">
<div class="popup_btn">
<span class="flr" id="${dtlDTO.ctznSttemntDetailSn}" >
<a href="#" class="btn blue" onclick="fnBiz.save('${dtlDTO.ctznSttemntDetailSn}')">저장</a>
<a href="#" class="btn red" onclick="fnBiz.remove('${dtlDTO.ctznSttemntDetailSn}')">삭제</a>
</span>
</div>
<table class="tbl03">
<caption><c:out value="${bizName}"/> 상세</caption>
<colgroup>
@ -145,7 +150,7 @@
</td>
<th>차량번호</th>
<td>
<input type="text" name="vhcleNo" value='<c:out value="${dtlDTO.vhcleNo}"/>' readonly>
<input type="text" name="vhcleNo" value='<c:out value="${dtlDTO.vhcleNo}"/>'>
</td>
<th>단속ID</th>
<td>
@ -155,7 +160,7 @@
<tr>
<th>단속일시</th>
<td>
<fmt:parseDate value="${dtlDTO.regltDeTime}" var="regltDeTimet" pattern="yyyyMMddHHmmss"/>
<fmt:parseDate value="${dtlDTO.regltDeTime}" var="regltDeTime" pattern="yyyyMMddHHmmss"/>
<input type="text" name="regltDeTime" value='<fmt:formatDate value="${regltDeTime}" pattern="yyyy-MM-dd HH:mm:ss"/>' readonly>
</td>
<th>단속장소</th>
@ -199,7 +204,8 @@
</tbody>
</table>
</c:forEach>
</form>
</c:forEach>
</form>
@ -229,7 +235,7 @@
/**************************************************************************
* Global Variable
**************************************************************************/
// let orgData;
let orgDtlDatas = [];
var imageEditorPopup = (flag, params) => fnBiz.pagePopup(flag, params);
var callbackReloadImage = () => fnBiz.downloadImg();
@ -279,9 +285,34 @@
window.opener.popup = CmmPopup.open(url, params, popOption, popTitle);
//var w = window.open("/imageEditor.do", "", "width=800,height=650,top=0px,left=200px,status=,resizable=false,scrollbars=no");
}
,viewImg: () => {
,save: (ctznSttemntDetailSn) => {
let idx = Number(ctznSttemntDetailSn);
if(orgDtlDatas[idx-1] == $('form[name=frmStmtDtl'+ctznSttemntDetailSn+']').serialize()){
alert('변경된 내용이 없습니다.');
return false;
}
const frm = $('form[name=frmStmtDtl'+ctznSttemntDetailSn+']');
const data = {
interfaceSeqN: '${reqDTO.interfaceSeqN}'
,ctznSttemntDetailSn: ctznSttemntDetailSn
,vhcleNo: frm.find('input[name=vhcleNo]').val().trim()
}
cmmBizAjax('modify', {
url: '<c:url value="/fims/biz/ec/modifyCtznStmtDtl.do"/>'
,data: $.param(data)
})
}
,remove: (ctznSttemntDetailSn) => {
const data = {
interfaceSeqN: '${reqDTO.interfaceSeqN}'
,ctznSttemntDetailSn: ctznSttemntDetailSn
}
if(confirm('첨부파일도 함께 삭제 하시겠습까?')) data.removeImageData = true;
cmmBizAjax('remove', {
url: '<c:url value="/fims/biz/ec/removeCtznStmtDtl.do"/>'
,data: $.param(data)
})
}
};
@ -298,10 +329,12 @@
* initialize
**************************************************************************/
$(document).ready(function () {
// orgData = $('form').serialize();
fnBiz.downloadImg();
dragable();
<c:forEach var="dtlDTO" items="${ctznStmtDtlDTOs}" varStatus="status">
orgDtlDatas.push($('form[name=frmStmtDtl${dtlDTO.ctznSttemntDetailSn}]').serialize())
</c:forEach>
fnBiz.downloadImg();
});

@ -70,7 +70,7 @@ function sttemntImgDownload(cmmFileDtls, appendElementId, reqDTO, isEditor) {
imgEl.appendChild(x);
document.querySelector('#imgList').appendChild(imgEl);
})
dragable();
//<img src="/resources/framework/images/common/noImage.png"/>
}

Loading…
Cancel
Save