wxd 2 years ago
parent
commit
04339c210f
  1. 12
      src/Shentun.WebPeis.Application/AppointPatientRegisters/AppointPatientRegisterAppService.cs
  2. 57
      src/Shentun.WebPeis.Application/Persons/PersonAppService.cs
  3. 1
      src/Shentun.WebPeis.Application/Shentun.WebPeis.Application.csproj
  4. 11
      src/Shentun.WebPeis.Application/WebPeisApplicationModule.cs
  5. 30
      src/Shentun.WebPeis.Domain/AppointPatientRegisters/AppointPatientRegisterManager.cs
  6. 5
      src/Shentun.WebPeis.Domain/Models/CustomerOrgGroup.cs
  7. 9
      src/Shentun.WebPeis.EntityFrameworkCore/Configures/CustomerOrgGroupConfigure.cs
  8. 4
      src/Shentun.WebPeis.HttpApi.Host/appsettings.json

12
src/Shentun.WebPeis.Application/AppointPatientRegisters/AppointPatientRegisterAppService.cs

@ -296,7 +296,8 @@ namespace Shentun.WebPeis.AppointPatientRegisters
on appointRegisterAsbitem.AsbitemId equals asbitem.AsbitemId on appointRegisterAsbitem.AsbitemId equals asbitem.AsbitemId
join customerOrg in await _customerOrgRepository.GetQueryableAsync() join customerOrg in await _customerOrgRepository.GetQueryableAsync()
on appointPatientRegister.CustomerOrgId equals customerOrg.CustomerOrgId on appointPatientRegister.CustomerOrgId equals customerOrg.CustomerOrgId
where appointPatientRegister.PersonId == input.PersonId
where appointPatientRegister.PersonId == input.PersonId &&
appointPatientRegister.CompleteFlag != AppointPatientRegisterCompleteFlag.CancelAppoint
orderby appointPatientRegister.AppointDate orderby appointPatientRegister.AppointDate
select new select new
{ {
@ -448,6 +449,7 @@ namespace Shentun.WebPeis.AppointPatientRegisters
[HttpPost("api/app/AppointPatientRegister/GetCustomerOrgAppointPatientRegisterByPersonId")] [HttpPost("api/app/AppointPatientRegister/GetCustomerOrgAppointPatientRegisterByPersonId")]
public async Task<PatientRegisterDto> GetCustomerOrgAppointPatientRegisterByPersonIdAsync(PersonIdInputDto input) public async Task<PatientRegisterDto> GetCustomerOrgAppointPatientRegisterByPersonIdAsync(PersonIdInputDto input)
{ {
var patientRegisterDto = (from user in await _identityUserRepository.GetQueryableAsync() var patientRegisterDto = (from user in await _identityUserRepository.GetQueryableAsync()
join person in await _personRepository.GetQueryableAsync() join person in await _personRepository.GetQueryableAsync()
on user.Id equals person.PersonId on user.Id equals person.PersonId
@ -482,6 +484,14 @@ namespace Shentun.WebPeis.AppointPatientRegisters
var customerOrgEntity = await _customerOrgRepository.GetAsync(o => o.CustomerOrgId == patientRegisterDto.CustomerOrgId); var customerOrgEntity = await _customerOrgRepository.GetAsync(o => o.CustomerOrgId == patientRegisterDto.CustomerOrgId);
customerOrgEntity = await _customerOrgRepository.GetAsync(o => o.PathCode == customerOrgEntity.PathCode.Substring(0, 5)); customerOrgEntity = await _customerOrgRepository.GetAsync(o => o.PathCode == customerOrgEntity.PathCode.Substring(0, 5));
patientRegisterDto.CustomerOrgName = customerOrgEntity.CustomerOrgName; patientRegisterDto.CustomerOrgName = customerOrgEntity.CustomerOrgName;
var appointPatientRegisters = await _repository.GetListAsync(o => o.PersonId == input.PersonId &&
o.AppointDate >= DateTime.Now.Date && o.CustomerOrgRegisterId == patientRegisterDto.CustomerOrgRegisterId);
if (appointPatientRegisters .Any())
{
throw new UserFriendlyException("已有今天及之后的团检预约订单,必须先取消订单才能重新预约");
}
return patientRegisterDto; return patientRegisterDto;
} }

57
src/Shentun.WebPeis.Application/Persons/PersonAppService.cs

@ -71,7 +71,7 @@ namespace Shentun.WebPeis.Persons
IRepository<PatientRegister> patientRegisterRepository, IRepository<PatientRegister> patientRegisterRepository,
IRepository<Patient> patientRepository, IRepository<Patient> patientRepository,
CacheService cacheService, CacheService cacheService,
//IHttpContextAccessor httpContextAccessor,
IHttpContextAccessor httpContextAccessor,
IRepository<CustomerOrg> customerOrgRepository) IRepository<CustomerOrg> customerOrgRepository)
{ {
_repository = repository; _repository = repository;
@ -85,7 +85,7 @@ namespace Shentun.WebPeis.Persons
_patientRegisterRepository = patientRegisterRepository; _patientRegisterRepository = patientRegisterRepository;
_patientRepository = patientRepository; _patientRepository = patientRepository;
_cacheService = cacheService; _cacheService = cacheService;
//_httpContextAccessor = httpContextAccessor;
_httpContextAccessor = httpContextAccessor;
_customerOrgRepository = customerOrgRepository; _customerOrgRepository = customerOrgRepository;
} }
@ -130,11 +130,14 @@ namespace Shentun.WebPeis.Persons
var dicStr = dic.Select(m => m.Key + "=" + m.Value).DefaultIfEmpty().Aggregate((m, n) => m + "&" + n); var dicStr = dic.Select(m => m.Key + "=" + m.Value).DefaultIfEmpty().Aggregate((m, n) => m + "&" + n);
var token = await GetTokenAsync(dicStr); var token = await GetTokenAsync(dicStr);
var options = new DistributedCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromDays(3));
var sessionKey = CacheKeys.SessionKey + Guid.NewGuid().ToString(); var sessionKey = CacheKeys.SessionKey + Guid.NewGuid().ToString();
var sessionKeyValue = Guid.NewGuid().ToString(); var sessionKeyValue = Guid.NewGuid().ToString();
_cache.Set(sessionKey, sessionKeyValue);
_cache.Set(sessionKey, sessionKeyValue, options);
token.SessionKey = sessionKey; token.SessionKey = sessionKey;
token.SessionKeyValue = sessionKeyValue; token.SessionKeyValue = sessionKeyValue;
return token; return token;
} }
@ -187,7 +190,7 @@ namespace Shentun.WebPeis.Persons
{ {
throw new UserFriendlyException("该身份证号已注册,但手机号码不一致"); throw new UserFriendlyException("该身份证号已注册,但手机号码不一致");
} }
if(!string.IsNullOrWhiteSpace(person.WechatOpenId))
if (!string.IsNullOrWhiteSpace(person.WechatOpenId))
{ {
throw new UserFriendlyException("该微信号已注册"); throw new UserFriendlyException("该微信号已注册");
} }
@ -267,12 +270,12 @@ namespace Shentun.WebPeis.Persons
throw new UserFriendlyException("无效的短信校验码或已过期"); throw new UserFriendlyException("无效的短信校验码或已过期");
} }
var person = await _repository.FindAsync(o=>o.IdNo == input.IdNo);
var person = await _repository.FindAsync(o => o.IdNo == input.IdNo);
if (person != null) if (person != null)
{ {
var user = (await _identityUserRepository.GetQueryableAsync()).Where(o =>o.Id == person.PersonId &&
o.PhoneNumber == input.MobileTelephone).FirstOrDefault();
if(user == null)
var user = (await _identityUserRepository.GetQueryableAsync()).Where(o => o.Id == person.PersonId &&
o.PhoneNumber == input.MobileTelephone).FirstOrDefault();
if (user == null)
{ {
throw new UserFriendlyException("该身份证号已注册,但手机号码不一致"); throw new UserFriendlyException("该身份证号已注册,但手机号码不一致");
} }
@ -310,11 +313,11 @@ namespace Shentun.WebPeis.Persons
} }
/// <summary>
/// 获取体检次数列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <summary>
/// 获取体检次数列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("api/app/Person/GetMedicalTimesListByPersonId")] [HttpPost("api/app/Person/GetMedicalTimesListByPersonId")]
public async Task<List<PersonMedicalTimesDto>> GetMedicalTimesListByPersonIdAsync(PersonIdInputDto input) public async Task<List<PersonMedicalTimesDto>> GetMedicalTimesListByPersonIdAsync(PersonIdInputDto input)
{ {
@ -323,11 +326,11 @@ namespace Shentun.WebPeis.Persons
join person in await _repository.GetQueryableAsync() join person in await _repository.GetQueryableAsync()
on user.Id equals person.PersonId on user.Id equals person.PersonId
join patient in await _patientRepository.GetQueryableAsync() join patient in await _patientRepository.GetQueryableAsync()
on new { idNo = person.IdNo ,phone = user.PhoneNumber} equals new { idNo = patient.IdNo, phone = patient.MobileTelephone }
on new { idNo = person.IdNo, phone = user.PhoneNumber } equals new { idNo = patient.IdNo, phone = patient.MobileTelephone }
join patientRegister in await _patientRegisterRepository.GetQueryableAsync() join patientRegister in await _patientRegisterRepository.GetQueryableAsync()
on patient.PatientId equals patientRegister.PatientId on patient.PatientId equals patientRegister.PatientId
where user.Id == input.PersonId && where user.Id == input.PersonId &&
( patientRegister.CompleteFlag == PatientRegisterCompleteFlag.Audit)
(patientRegister.CompleteFlag == PatientRegisterCompleteFlag.Audit)
orderby patientRegister.MedicalStartDate orderby patientRegister.MedicalStartDate
select new PersonMedicalTimesDto() select new PersonMedicalTimesDto()
{ {
@ -368,7 +371,7 @@ namespace Shentun.WebPeis.Persons
}).ToList(); }).ToList();
for(var i = 0; i<personList.Count;i++)
for (var i = 0; i < personList.Count; i++)
{ {
personList[i].DisplayOrder = i + 1; personList[i].DisplayOrder = i + 1;
if (personList[i].PersonId == CurrentUser.Id) if (personList[i].PersonId == CurrentUser.Id)
@ -376,7 +379,7 @@ namespace Shentun.WebPeis.Persons
personList[i].DisplayOrder = 0; //本人强行排第一个 personList[i].DisplayOrder = 0; //本人强行排第一个
} }
} }
personList = personList.OrderBy(o=>o.DisplayOrder).ToList();
personList = personList.OrderBy(o => o.DisplayOrder).ToList();
return personList; return personList;
} }
/// <summary> /// <summary>
@ -388,17 +391,17 @@ namespace Shentun.WebPeis.Persons
[HttpPost("api/app/Person/GetMedicalReportByPatientRegisterId")] [HttpPost("api/app/Person/GetMedicalReportByPatientRegisterId")]
public async Task<MedicalReportDto> GetMedicalReportByPatientRegisterIdAsync(PatientRegisterIdInputDto input) public async Task<MedicalReportDto> GetMedicalReportByPatientRegisterIdAsync(PatientRegisterIdInputDto input)
{ {
var entity = await _patientRegisterRepository.GetAsync(o=>o.PatientRegisterId == input.PatientRegisterId);
if(string.IsNullOrWhiteSpace(entity.ReportFile))
var entity = await _patientRegisterRepository.GetAsync(o => o.PatientRegisterId == input.PatientRegisterId);
if (string.IsNullOrWhiteSpace(entity.ReportFile))
{ {
throw new UserFriendlyException("没有报告单"); throw new UserFriendlyException("没有报告单");
} }
var Host = $"{_httpContextAccessor.HttpContext.Request.Scheme}://{_httpContextAccessor.HttpContext.Request.Host.Host}:{ _httpContextAccessor.HttpContext.Request.Host.Port}";
var Host = $"{_httpContextAccessor.HttpContext.Request.Scheme}://{_httpContextAccessor.HttpContext.Request.Host.Host}:{_httpContextAccessor.HttpContext.Request.Host.Port}";
var returnValue = new MedicalReportDto() var returnValue = new MedicalReportDto()
{ {
FilePath = entity.ReportFile, FilePath = entity.ReportFile,
FileBase64 = Shentun.Utilities.FileHelper.ToBase64(Host + entity.ReportFile) FileBase64 = Shentun.Utilities.FileHelper.ToBase64(Host + entity.ReportFile)
};
};
return returnValue; return returnValue;
} }
@ -412,7 +415,7 @@ namespace Shentun.WebPeis.Persons
[HttpPost("api/app/Person/GetSmsVerifyCode")] [HttpPost("api/app/Person/GetSmsVerifyCode")]
public async Task<SmsVerifyCodeDto> GetSmsVerifyCodeAsync(SmsVerifyCodeInputDto input) public async Task<SmsVerifyCodeDto> GetSmsVerifyCodeAsync(SmsVerifyCodeInputDto input)
{ {
if(input == null)
if (input == null)
{ {
throw new UserFriendlyException("input不能为空"); throw new UserFriendlyException("input不能为空");
} }
@ -463,7 +466,7 @@ namespace Shentun.WebPeis.Persons
if (person != null) if (person != null)
{ {
var user = (await _identityUserRepository.GetQueryableAsync()).Where(o => o.Id == person.PersonId && var user = (await _identityUserRepository.GetQueryableAsync()).Where(o => o.Id == person.PersonId &&
o.PhoneNumber == input.MobileTelephone).FirstOrDefault();
o.PhoneNumber == input.MobileTelephone).FirstOrDefault();
if (user == null) if (user == null)
{ {
throw new UserFriendlyException("该身份证号已注册,但手机号码不一致"); throw new UserFriendlyException("该身份证号已注册,但手机号码不一致");
@ -517,7 +520,7 @@ namespace Shentun.WebPeis.Persons
OpenId = wechatUserDto.OpenId OpenId = wechatUserDto.OpenId
}; };
var options = new DistributedCacheEntryOptions() var options = new DistributedCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(720));
.SetAbsoluteExpiration(TimeSpan.FromDays(3));
_cache.Set(CacheKeys.OpenIdKey + wechatUserDto.OpenId, _cache.Set(CacheKeys.OpenIdKey + wechatUserDto.OpenId,
wechatUserDto.OpenId, options); wechatUserDto.OpenId, options);
@ -557,7 +560,7 @@ namespace Shentun.WebPeis.Persons
public async Task<string> SendVerifySms(CreateSmsTaskDto createSmsTaskDto) public async Task<string> SendVerifySms(CreateSmsTaskDto createSmsTaskDto)
{ {
if(createSmsTaskDto == null)
if (createSmsTaskDto == null)
{ {
throw new UserFriendlyException("createSmsTaskDto参数不能为空"); throw new UserFriendlyException("createSmsTaskDto参数不能为空");
} }
@ -571,14 +574,14 @@ namespace Shentun.WebPeis.Persons
throw new Exception("解析校验短信有效时间错误"); throw new Exception("解析校验短信有效时间错误");
} }
createSmsTaskDto.Content = message+"|" + verifySmsValidTime.ToString();
createSmsTaskDto.Content = message + "|" + verifySmsValidTime.ToString();
//发送短信 //发送短信
createSmsTaskDto.TaskCycleType = '0'; createSmsTaskDto.TaskCycleType = '0';
await SmsClientHelper.CreateVerifySmsTask(createSmsTaskDto); await SmsClientHelper.CreateVerifySmsTask(createSmsTaskDto);
//存储短信校验码 //存储短信校验码
var options = new DistributedCacheEntryOptions() var options = new DistributedCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(verifySmsValidTime)); .SetAbsoluteExpiration(TimeSpan.FromMinutes(verifySmsValidTime));
var smsVerifyCodeKey = CacheKeys.SmsKey + createSmsTaskDto.MobileTelephone + Guid.NewGuid().ToString();
var smsVerifyCodeKey = CacheKeys.SmsKey + createSmsTaskDto.MobileTelephone + Guid.NewGuid().ToString();
_cache.Set(smsVerifyCodeKey, message, options); _cache.Set(smsVerifyCodeKey, message, options);
return smsVerifyCodeKey; return smsVerifyCodeKey;
} }

1
src/Shentun.WebPeis.Application/Shentun.WebPeis.Application.csproj

@ -21,6 +21,7 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" />
<PackageReference Include="Volo.Abp.Account.Application" Version="8.1.3" /> <PackageReference Include="Volo.Abp.Account.Application" Version="8.1.3" />
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="8.1.3" />
<PackageReference Include="Volo.Abp.Identity.Application" Version="8.1.3" /> <PackageReference Include="Volo.Abp.Identity.Application" Version="8.1.3" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="8.1.3" /> <PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="8.1.3" />
<PackageReference Include="Volo.Abp.TenantManagement.Application" Version="8.1.3" /> <PackageReference Include="Volo.Abp.TenantManagement.Application" Version="8.1.3" />

11
src/Shentun.WebPeis.Application/WebPeisApplicationModule.cs

@ -3,6 +3,8 @@ using Shentun.Peis.OrganizationUnits;
using Shentun.WebPeis.Users; using Shentun.WebPeis.Users;
using Volo.Abp.Account; using Volo.Abp.Account;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.FeatureManagement; using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
@ -20,7 +22,8 @@ namespace Shentun.WebPeis;
typeof(AbpPermissionManagementApplicationModule), typeof(AbpPermissionManagementApplicationModule),
typeof(AbpTenantManagementApplicationModule), typeof(AbpTenantManagementApplicationModule),
typeof(AbpFeatureManagementApplicationModule), typeof(AbpFeatureManagementApplicationModule),
typeof(AbpSettingManagementApplicationModule)
typeof(AbpSettingManagementApplicationModule),
typeof(AbpCachingStackExchangeRedisModule)
)] )]
public class WebPeisApplicationModule : AbpModule public class WebPeisApplicationModule : AbpModule
{ {
@ -32,5 +35,11 @@ public class WebPeisApplicationModule : AbpModule
}); });
context.Services.AddTransient<OrganizationUnitsAppService>(); context.Services.AddTransient<OrganizationUnitsAppService>();
context.Services.AddTransient<UserAppService>(); context.Services.AddTransient<UserAppService>();
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = "WebPeis";
options.HideErrors = false;
});
} }
} }

30
src/Shentun.WebPeis.Domain/AppointPatientRegisters/AppointPatientRegisterManager.cs

@ -66,6 +66,7 @@ namespace Shentun.WebPeis.AppointPatientRegisters
public async Task<AppointPatientRegister> CreateAsync(AppointPatientRegister entity) public async Task<AppointPatientRegister> CreateAsync(AppointPatientRegister entity)
{ {
await Verify(entity); await Verify(entity);
entity.AppointPatientRegisterId = GuidGenerator.Create(); entity.AppointPatientRegisterId = GuidGenerator.Create();
entity.CompleteFlag = AppointPatientRegisterCompleteFlag.Appoint; entity.CompleteFlag = AppointPatientRegisterCompleteFlag.Appoint;
entity.IsCharge = 'N'; entity.IsCharge = 'N';
@ -178,8 +179,29 @@ namespace Shentun.WebPeis.AppointPatientRegisters
throw new UserFriendlyException("单位分组项目不能取消"); throw new UserFriendlyException("单位分组项目不能取消");
} }
}); });
foreach(var asbitem in entity.AppointRegisterAsbitems)
{
decimal addMoney = 0;
var customerOrgGroupDetail = customerOrgGroupDetails.Where(o => o.AsbitemId == asbitem.AsbitemId).FirstOrDefault();
if(customerOrgGroupDetail != null)
{
addMoney += customerOrgGroupDetail.Price;
}
if(addMoney > 0)
{
var customerOrgGroup = await _customerOrgGroupRepository.GetAsync(o => o.CustomerOrgGroupId == entity.CustomerOrgGroupId);
if(addMoney > customerOrgGroup.CanAddMoney)
{
throw new UserFriendlyException($"自选的单位支付金额能超过{customerOrgGroup.CanAddMoney}元");
}
}
}
} }
var asbitems = await _asbitemRepository.GetListAsync(); var asbitems = await _asbitemRepository.GetListAsync();
foreach (var appointRegisterAsbitem in entity.AppointRegisterAsbitems) foreach (var appointRegisterAsbitem in entity.AppointRegisterAsbitems)
{ {
@ -214,6 +236,14 @@ namespace Shentun.WebPeis.AppointPatientRegisters
appointRegisterAsbitem.IsCharge = 'N'; appointRegisterAsbitem.IsCharge = 'N';
} }
var appoentPatientRegisters = await _repository.GetListAsync(o => o.PersonId == entity.PersonId &&
o.AppointDate >= DateTime.Now.Date);
if(appoentPatientRegisters.Count > 1)
{
throw new UserFriendlyException("已有今天及之后的预约订单,必须先取消订单才能重新预约");
}
} }
} }
} }

5
src/Shentun.WebPeis.Domain/Models/CustomerOrgGroup.cs

@ -63,6 +63,11 @@ public partial class CustomerOrgGroup : AuditedEntity, IHasConcurrencyStamp
public int DisplayOrder { get; set; } public int DisplayOrder { get; set; }
public Guid CustomerOrgRegisterId { get; set; } public Guid CustomerOrgRegisterId { get; set; }
/// <summary>
/// 可增加单位支付金额
/// </summary>
public decimal CanAddMoney { get; set; }
public string? ConcurrencyStamp { get; set; } public string? ConcurrencyStamp { get; set; }

9
src/Shentun.WebPeis.EntityFrameworkCore/Configures/CustomerOrgGroupConfigure.cs

@ -69,12 +69,19 @@ namespace Shentun.WebPeis.Configures
entity.Property(e => e.Price) entity.Property(e => e.Price)
.HasPrecision(10, 2) .HasPrecision(10, 2)
.HasComment("价格") .HasComment("价格")
.HasColumnName("price");
.HasColumnName("price")
.HasDefaultValueSql("0"); ;
entity.Property(e => e.Remark) entity.Property(e => e.Remark)
.HasMaxLength(100) .HasMaxLength(100)
.HasComment("备注") .HasComment("备注")
.HasColumnName("remark"); .HasColumnName("remark");
entity.Property(e => e.CanAddMoney)
.HasPrecision(10, 2)
.HasColumnName("can_add_money")
.HasComment("可增加单位支付金额")
.IsRequired().HasDefaultValueSql("0");
entity.HasOne(d => d.CustomerOrgRegister).WithMany(p => p.CustomerOrgGroups) entity.HasOne(d => d.CustomerOrgRegister).WithMany(p => p.CustomerOrgGroups)
.HasForeignKey(d => d.CustomerOrgRegisterId) .HasForeignKey(d => d.CustomerOrgRegisterId)
.OnDelete(DeleteBehavior.ClientSetNull) .OnDelete(DeleteBehavior.ClientSetNull)

4
src/Shentun.WebPeis.HttpApi.Host/appsettings.json

@ -55,5 +55,9 @@
"VerifySmsTypeId": "3a12e09d-438a-1d08-d4f0-712aef13708d", "VerifySmsTypeId": "3a12e09d-438a-1d08-d4f0-712aef13708d",
"VerifySmsValidTime": "1", "VerifySmsValidTime": "1",
"SmsAppId": "3a12e09f-8534-ec43-8c46-e61af0964ab6" "SmsAppId": "3a12e09f-8534-ec43-8c46-e61af0964ab6"
},
"Redis": {
"IsEnabled": "true",
"Configuration": "127.0.0.1"
} }
} }
Loading…
Cancel
Save