Yeni Müşteri ve Eski Müşteri Sipariş ayrımı
This commit is contained in:
parent
bab639ce63
commit
5c9c99206c
20 changed files with 1819 additions and 120 deletions
|
|
@ -9,6 +9,9 @@ public class CountryDto : AuditedEntityDto<string>
|
|||
public string GroupName { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public int PhoneCode { get; set; }
|
||||
public int? PhoneNumberMinLength { get; set; }
|
||||
public int? PhoneNumberMaxLength { get; set; }
|
||||
public string? PhoneNumberFormat { get; set; }
|
||||
public string TaxLabel { get; set; }
|
||||
public bool ZipRequired { get; set; }
|
||||
public bool StateRequired { get; set; }
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ public class OrderDto : EntityDto<Guid>
|
|||
|
||||
public List<OrderItemDto> Items { get; set; }
|
||||
|
||||
public string BillingCycle { get; set; }
|
||||
public int Period { get; set; }
|
||||
public DateTime? LicenseStartTime { get; set; }
|
||||
public DateTime? LicenseStopTime { get; set; }
|
||||
|
||||
public decimal Subtotal { get; set; }
|
||||
public decimal Commission { get; set; }
|
||||
public decimal Total { get; set; }
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ public class CustomTenantDto
|
|||
{
|
||||
public string Name { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool IsExisting { get; set; }
|
||||
|
||||
public Guid? Id { get; set; }
|
||||
public string OrganizationName { get; set; }
|
||||
|
|
|
|||
|
|
@ -76,8 +76,10 @@ public partial class CreateUpdateOrganizationUnitDtoToOrganizationUnitMapper : M
|
|||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||
public partial class TenantToCustomTenantDtoMapper : MapperBase<Tenant, CustomTenantDto>
|
||||
{
|
||||
[MapperIgnoreTarget(nameof(CustomTenantDto.IsExisting))]
|
||||
public override partial CustomTenantDto Map(Tenant source);
|
||||
|
||||
[MapperIgnoreTarget(nameof(CustomTenantDto.IsExisting))]
|
||||
public override partial void Map(Tenant source, CustomTenantDto destination);
|
||||
|
||||
public override void BeforeMap(Tenant source)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ public class PublicAppService : PlatformAppService
|
|||
private readonly IRepository<Home, Guid> _homeRepository;
|
||||
private readonly IRepository<Contact, Guid> _contactRepository;
|
||||
private readonly IRepository<Country, string> _countryRepository;
|
||||
private readonly IRepository<City, Guid> _cityRepository;
|
||||
private readonly IRepository<District, Guid> _districtRepository;
|
||||
private readonly IIdentityUserRepository _identityUserRepository;
|
||||
private readonly IRepository<LanguageKey, Guid> _languageKeyRepository;
|
||||
private readonly IRepository<LanguageText, Guid> _languageTextRepository;
|
||||
|
|
@ -54,6 +56,8 @@ public class PublicAppService : PlatformAppService
|
|||
IRepository<Home, Guid> homeRepository,
|
||||
IRepository<Contact, Guid> contactRepository,
|
||||
IRepository<Country, string> countryRepository,
|
||||
IRepository<City, Guid> cityRepository,
|
||||
IRepository<District, Guid> districtRepository,
|
||||
IIdentityUserRepository identityUserRepository,
|
||||
IRepository<LanguageKey, Guid> languageKeyRepository,
|
||||
IRepository<LanguageText, Guid> languageTextRepository,
|
||||
|
|
@ -78,6 +82,8 @@ public class PublicAppService : PlatformAppService
|
|||
_languageTextRepository = languageTextRepository;
|
||||
_languageTextAppService = languageTextAppService;
|
||||
_countryRepository = countryRepository;
|
||||
_cityRepository = cityRepository;
|
||||
_districtRepository = districtRepository;
|
||||
}
|
||||
|
||||
public async Task<List<ServiceDto>> GetServicesListAsync()
|
||||
|
|
@ -457,10 +463,14 @@ public class PublicAppService : PlatformAppService
|
|||
|
||||
public async Task<OrderDto> CreateOrderAsync(OrderDto input)
|
||||
{
|
||||
var billingCycle = input.BillingCycle.IsNullOrWhiteSpace() ? "yearly" : input.BillingCycle;
|
||||
var period = input.Period > 0 ? input.Period : 1;
|
||||
|
||||
var entity = new Order()
|
||||
{
|
||||
Name = input.Tenant.Name,
|
||||
IsActive = input.Tenant.IsActive,
|
||||
IsExisting = input.Tenant.IsExisting,
|
||||
|
||||
OrganizationName = input.Tenant.OrganizationName,
|
||||
Founder = input.Tenant.Founder,
|
||||
|
|
@ -479,6 +489,10 @@ public class PublicAppService : PlatformAppService
|
|||
Website = input.Tenant.Website,
|
||||
MenuGroup = input.Tenant.MenuGroup,
|
||||
|
||||
BillingCycle = billingCycle,
|
||||
Period = period,
|
||||
LicenseStartTime = input.LicenseStartTime,
|
||||
LicenseStopTime = input.LicenseStopTime,
|
||||
Subtotal = input.Subtotal,
|
||||
Commission = input.Commission,
|
||||
Total = input.Total,
|
||||
|
|
@ -526,9 +540,75 @@ public class PublicAppService : PlatformAppService
|
|||
|
||||
public async Task<List<CountryDto>> GetCountryAsync()
|
||||
{
|
||||
var entities = await _countryRepository.GetListAsync() ?? throw new EntityNotFoundException(typeof(Country));
|
||||
var queryable = await _countryRepository.GetQueryableAsync();
|
||||
|
||||
return ObjectMapper.Map<List<Country>, List<CountryDto>>(entities);
|
||||
return await AsyncExecuter.ToListAsync(
|
||||
queryable
|
||||
.OrderBy(country => country.Name)
|
||||
.Select(country => new CountryDto
|
||||
{
|
||||
Id = country.Id,
|
||||
Name = country.Name,
|
||||
GroupName = country.GroupName,
|
||||
Currency = country.Currency,
|
||||
PhoneCode = country.PhoneCode,
|
||||
PhoneNumberMinLength = country.PhoneNumberMinLength,
|
||||
PhoneNumberMaxLength = country.PhoneNumberMaxLength,
|
||||
PhoneNumberFormat = country.PhoneNumberFormat,
|
||||
TaxLabel = country.TaxLabel,
|
||||
ZipRequired = country.ZipRequired,
|
||||
StateRequired = country.StateRequired,
|
||||
Cities = new List<CityDto>()
|
||||
}));
|
||||
}
|
||||
|
||||
public async Task<List<CityDto>> GetCityAsync(string country)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(country))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var queryable = await _cityRepository.GetQueryableAsync();
|
||||
|
||||
return await AsyncExecuter.ToListAsync(
|
||||
queryable
|
||||
.Where(city => city.Country == country)
|
||||
.OrderBy(city => city.Name)
|
||||
.Select(city => new CityDto
|
||||
{
|
||||
Id = city.Id,
|
||||
Country = city.Country,
|
||||
Name = city.Name,
|
||||
PlateCode = city.PlateCode
|
||||
}));
|
||||
}
|
||||
|
||||
public async Task<List<DistrictDto>> GetDistrictAsync(string country, string city)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(country) || string.IsNullOrWhiteSpace(city))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var queryable = await _districtRepository.GetQueryableAsync();
|
||||
var entities = await AsyncExecuter.ToListAsync(
|
||||
queryable
|
||||
.Where(district => district.Country == country && district.City == city)
|
||||
.OrderBy(district => district.Name));
|
||||
|
||||
return entities
|
||||
.GroupBy(district => district.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(group => group.First())
|
||||
.Select(district => new DistrictDto
|
||||
{
|
||||
Id = district.Id,
|
||||
Country = district.Country,
|
||||
City = district.City,
|
||||
Name = district.Name,
|
||||
Township = district.Township
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task SaveContactPageAsync(SaveContactPageInput input)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -248,6 +248,9 @@ public class HostDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
groupName,
|
||||
item.Currency,
|
||||
item.PhoneCode,
|
||||
item.PhoneNumberMinLength,
|
||||
item.PhoneNumberMaxLength,
|
||||
item.PhoneNumberFormat,
|
||||
item.TaxLabel
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -13758,6 +13758,24 @@
|
|||
"en": "Billing Cycle",
|
||||
"tr": "Fatura Döngüsü"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.Period",
|
||||
"en": "Period",
|
||||
"tr": "Periyot"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.LicenseStartTime",
|
||||
"en": "License Start Time",
|
||||
"tr": "Lisans Başlangıç Tarihi"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.LicenseStopTime",
|
||||
"en": "License Stop Time",
|
||||
"tr": "Lisans Bitiş Tarihi"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.Abbreviation",
|
||||
|
|
@ -16068,6 +16086,24 @@
|
|||
"en": "Phone Code",
|
||||
"tr": "Telefon Kodu"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.PhoneNumberMinLength",
|
||||
"en": "Phone Number Min Length",
|
||||
"tr": "Telefon Numarası Minimum Uzunluk"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.PhoneNumberMaxLength",
|
||||
"en": "Phone Number Max Length",
|
||||
"tr": "Telefon Numarası Maksimum Uzunluk"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.PhoneNumberFormat",
|
||||
"en": "Phone Number Format",
|
||||
"tr": "Telefon Numarası Formatı"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.Place",
|
||||
|
|
@ -16140,6 +16176,12 @@
|
|||
"en": "Postal Code",
|
||||
"tr": "Posta Kodu"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.IsExisting",
|
||||
"en": "Existing Customer",
|
||||
"tr": "Mevcut Musteri"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Listform.ListformField.Priority",
|
||||
|
|
|
|||
|
|
@ -1766,9 +1766,12 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
new EditingFormItemDto { Order = 3, DataField = "GroupName", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order = 4, DataField = "Currency", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order = 5, DataField = "PhoneCode", ColSpan = 1, EditorType2 = EditorTypes.dxNumberBox },
|
||||
new EditingFormItemDto { Order = 6, DataField = "TaxLabel", ColSpan = 1, EditorType2 = EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order = 7, DataField = "ZipRequired", ColSpan = 1, EditorType2 = EditorTypes.dxCheckBox },
|
||||
new EditingFormItemDto { Order = 8, DataField = "StateRequired", ColSpan = 1, EditorType2 = EditorTypes.dxCheckBox }
|
||||
new EditingFormItemDto { Order = 6, DataField = "PhoneNumberMinLength", ColSpan = 1, EditorType2 = EditorTypes.dxNumberBox },
|
||||
new EditingFormItemDto { Order = 7, DataField = "PhoneNumberMaxLength", ColSpan = 1, EditorType2 = EditorTypes.dxNumberBox },
|
||||
new EditingFormItemDto { Order = 8, DataField = "PhoneNumberFormat", ColSpan = 1, EditorType2 = EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order = 9, DataField = "TaxLabel", ColSpan = 1, EditorType2 = EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order = 10, DataField = "ZipRequired", ColSpan = 1, EditorType2 = EditorTypes.dxCheckBox },
|
||||
new EditingFormItemDto { Order = 11, DataField = "StateRequired", ColSpan = 1, EditorType2 = EditorTypes.dxCheckBox }
|
||||
]
|
||||
}
|
||||
}),
|
||||
|
|
@ -1866,6 +1869,57 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.Int32,
|
||||
FieldName = "PhoneNumberMinLength",
|
||||
CaptionName = "App.Listform.ListformField.PhoneNumberMinLength",
|
||||
Width = 0,
|
||||
ListOrderNo = 7,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = false,
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.Int32,
|
||||
FieldName = "PhoneNumberMaxLength",
|
||||
CaptionName = "App.Listform.ListformField.PhoneNumberMaxLength",
|
||||
Width = 0,
|
||||
ListOrderNo = 8,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = false,
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.String,
|
||||
FieldName = "PhoneNumberFormat",
|
||||
CaptionName = "App.Listform.ListformField.PhoneNumberFormat",
|
||||
Width = 0,
|
||||
ListOrderNo = 9,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = false,
|
||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
|
|
@ -1873,7 +1927,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "TaxLabel",
|
||||
CaptionName = "App.Listform.ListformField.TaxLabel",
|
||||
Width = 0,
|
||||
ListOrderNo = 7,
|
||||
ListOrderNo = 10,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -1889,7 +1943,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "ZipRequired",
|
||||
CaptionName = "App.Listform.ListformField.ZipRequired",
|
||||
Width = 0,
|
||||
ListOrderNo = 8,
|
||||
ListOrderNo = 11,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -1904,7 +1958,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "StateRequired",
|
||||
CaptionName = "App.Listform.ListformField.StateRequired",
|
||||
Width = 0,
|
||||
ListOrderNo = 9,
|
||||
ListOrderNo = 12,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -6931,13 +6985,21 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
new EditingFormItemDto { Order = 18, DataField = "Total", ColSpan = 1, EditorType2 = EditorTypes.dxNumberBox, EditorOptions=EditorOptionValues.NumberStandartFormat() },
|
||||
new EditingFormItemDto { Order = 19, DataField = "PaymentMethodId", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order = 20, DataField = "Installment", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||
new EditingFormItemDto { Order = 21, DataField = "IsActive", ColSpan = 1, EditorType2 = EditorTypes.dxCheckBox },
|
||||
new EditingFormItemDto { Order = 21, DataField = "BillingCycle", ColSpan = 1, EditorType2 = EditorTypes.dxTextBox },
|
||||
new EditingFormItemDto { Order = 22, DataField = "Period", ColSpan = 1, EditorType2 = EditorTypes.dxNumberBox },
|
||||
new EditingFormItemDto { Order = 23, DataField = "LicenseStartTime", ColSpan = 1, EditorType2 = EditorTypes.dxDateBox },
|
||||
new EditingFormItemDto { Order = 24, DataField = "LicenseStopTime", ColSpan = 1, EditorType2 = EditorTypes.dxDateBox },
|
||||
new EditingFormItemDto { Order = 25, DataField = "IsExisting", ColSpan = 1, EditorType2 = EditorTypes.dxCheckBox },
|
||||
new EditingFormItemDto { Order = 26, DataField = "IsActive", ColSpan = 1, EditorType2 = EditorTypes.dxCheckBox },
|
||||
]
|
||||
}
|
||||
}),
|
||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||
new() { FieldName = "Country", FieldDbType = DbType.String, Value = "Türkiye", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "BillingCycle", FieldDbType = DbType.String, Value = "yearly", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "Period", FieldDbType = DbType.Int32, Value = "1", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "IsExisting", FieldDbType = DbType.Boolean, Value = "false", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "IsActive", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||
new() { FieldName = "Commission", FieldDbType = DbType.Decimal, Value = "0", CustomValueType = FieldCustomValueTypeEnum.Value }
|
||||
}),
|
||||
|
|
@ -7013,6 +7075,91 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.Boolean,
|
||||
FieldName = "IsExisting",
|
||||
CaptionName = "App.Listform.ListformField.IsExisting",
|
||||
Width = 0,
|
||||
ListOrderNo = 4,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.String,
|
||||
FieldName = "BillingCycle",
|
||||
CaptionName = "App.Listform.ListformField.BillingCycle",
|
||||
Width = 0,
|
||||
ListOrderNo = 5,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.Int32,
|
||||
FieldName = "Period",
|
||||
CaptionName = "App.Listform.ListformField.Period",
|
||||
Width = 0,
|
||||
ListOrderNo = 6,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.DateTime,
|
||||
FieldName = "LicenseStartTime",
|
||||
CaptionName = "App.Listform.ListformField.LicenseStartTime",
|
||||
Width = 0,
|
||||
ListOrderNo = 7,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.DateTime,
|
||||
FieldName = "LicenseStopTime",
|
||||
CaptionName = "App.Listform.ListformField.LicenseStopTime",
|
||||
Width = 0,
|
||||
ListOrderNo = 8,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
},
|
||||
new()
|
||||
{
|
||||
ListFormCode = listForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
|
|
@ -7020,7 +7167,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "VknTckn",
|
||||
CaptionName = "App.Listform.ListformField.VknTckn",
|
||||
Width = 0,
|
||||
ListOrderNo = 4,
|
||||
ListOrderNo = 9,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7038,7 +7185,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "TaxOffice",
|
||||
CaptionName = "App.Listform.ListformField.TaxOffice",
|
||||
Width = 0,
|
||||
ListOrderNo = 5,
|
||||
ListOrderNo = 10,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7056,7 +7203,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "Address1",
|
||||
CaptionName = "App.Listform.ListformField.Address1",
|
||||
Width = 0,
|
||||
ListOrderNo = 6,
|
||||
ListOrderNo = 11,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7074,7 +7221,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "Address2",
|
||||
CaptionName = "App.Listform.ListformField.Address2",
|
||||
Width = 0,
|
||||
ListOrderNo = 7,
|
||||
ListOrderNo = 12,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7091,7 +7238,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "Country",
|
||||
CaptionName = "App.Listform.ListformField.Country",
|
||||
Width = 0,
|
||||
ListOrderNo = 8,
|
||||
ListOrderNo = 13,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7117,7 +7264,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "City",
|
||||
CaptionName = "App.Listform.ListformField.City",
|
||||
Width = 0,
|
||||
ListOrderNo = 9,
|
||||
ListOrderNo = 14,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7146,7 +7293,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "District",
|
||||
CaptionName = "App.Listform.ListformField.District",
|
||||
Width = 0,
|
||||
ListOrderNo = 10,
|
||||
ListOrderNo = 15,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7174,7 +7321,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "PhoneNumber",
|
||||
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
|
||||
Width = 0,
|
||||
ListOrderNo = 11,
|
||||
ListOrderNo = 16,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7193,7 +7340,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "MobileNumber",
|
||||
CaptionName = "App.Listform.ListformField.MobileNumber",
|
||||
Width = 0,
|
||||
ListOrderNo = 12,
|
||||
ListOrderNo = 17,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7212,7 +7359,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "FaxNumber",
|
||||
CaptionName = "App.Listform.ListformField.FaxNumber",
|
||||
Width = 0,
|
||||
ListOrderNo = 13,
|
||||
ListOrderNo = 18,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7230,7 +7377,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "Website",
|
||||
CaptionName = "App.Listform.ListformField.Website",
|
||||
Width = 0,
|
||||
ListOrderNo = 14,
|
||||
ListOrderNo = 19,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7249,7 +7396,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
CaptionName = "App.Listform.ListformField.Subtotal",
|
||||
Alignment = "right",
|
||||
Width = 0,
|
||||
ListOrderNo = 15,
|
||||
ListOrderNo = 20,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7269,7 +7416,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
CaptionName = "App.Listform.ListformField.Commission",
|
||||
Alignment = "right",
|
||||
Width = 0,
|
||||
ListOrderNo = 16,
|
||||
ListOrderNo = 21,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7289,7 +7436,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
CaptionName = "App.Listform.ListformField.Total",
|
||||
Alignment = "right",
|
||||
Width = 0,
|
||||
ListOrderNo = 17,
|
||||
ListOrderNo = 22,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7308,7 +7455,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "PaymentMethodId",
|
||||
CaptionName = "App.Listform.ListformField.PaymentMethodId",
|
||||
Width = 0,
|
||||
ListOrderNo = 18,
|
||||
ListOrderNo = 23,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7327,7 +7474,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "Installment",
|
||||
CaptionName = "App.Listform.ListformField.Installment",
|
||||
Width = 0,
|
||||
ListOrderNo = 19,
|
||||
ListOrderNo = 24,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7346,7 +7493,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "IsActive",
|
||||
CaptionName = "App.Listform.ListformField.IsActive",
|
||||
Width = 0,
|
||||
ListOrderNo = 20,
|
||||
ListOrderNo = 25,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7363,7 +7510,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "PostalCode",
|
||||
CaptionName = "App.Listform.ListformField.PostalCode",
|
||||
Width = 0,
|
||||
ListOrderNo = 21,
|
||||
ListOrderNo = 26,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7381,7 +7528,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
FieldName = "Email",
|
||||
CaptionName = "App.Listform.ListformField.Email",
|
||||
Width = 0,
|
||||
ListOrderNo = 22,
|
||||
ListOrderNo = 27,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
|
|
@ -7394,6 +7541,133 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
|||
]);
|
||||
#endregion
|
||||
}
|
||||
else
|
||||
{
|
||||
var existingListForm = await _listFormRepository.FirstOrDefaultAsync(a => a.ListFormCode == listFormName);
|
||||
|
||||
if (existingListForm != null)
|
||||
{
|
||||
if (!await _listFormFieldRepository.AnyAsync(a =>
|
||||
a.ListFormCode == listFormName &&
|
||||
a.FieldName == "BillingCycle" &&
|
||||
a.CultureName == LanguageCodes.En))
|
||||
{
|
||||
await _listFormFieldRepository.InsertAsync(new()
|
||||
{
|
||||
ListFormCode = existingListForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.String,
|
||||
FieldName = "BillingCycle",
|
||||
CaptionName = "App.Listform.ListformField.BillingCycle",
|
||||
Width = 0,
|
||||
ListOrderNo = 20,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(existingListForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
});
|
||||
}
|
||||
|
||||
if (!await _listFormFieldRepository.AnyAsync(a =>
|
||||
a.ListFormCode == listFormName &&
|
||||
a.FieldName == "Period" &&
|
||||
a.CultureName == LanguageCodes.En))
|
||||
{
|
||||
await _listFormFieldRepository.InsertAsync(new()
|
||||
{
|
||||
ListFormCode = existingListForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.Int32,
|
||||
FieldName = "Period",
|
||||
CaptionName = "App.Listform.ListformField.Period",
|
||||
Width = 0,
|
||||
ListOrderNo = 21,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(existingListForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
});
|
||||
}
|
||||
|
||||
if (!await _listFormFieldRepository.AnyAsync(a =>
|
||||
a.ListFormCode == listFormName &&
|
||||
a.FieldName == "LicenseStartTime" &&
|
||||
a.CultureName == LanguageCodes.En))
|
||||
{
|
||||
await _listFormFieldRepository.InsertAsync(new()
|
||||
{
|
||||
ListFormCode = existingListForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.DateTime,
|
||||
FieldName = "LicenseStartTime",
|
||||
CaptionName = "App.Listform.ListformField.LicenseStartTime",
|
||||
Width = 0,
|
||||
ListOrderNo = 22,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(existingListForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
});
|
||||
}
|
||||
|
||||
if (!await _listFormFieldRepository.AnyAsync(a =>
|
||||
a.ListFormCode == listFormName &&
|
||||
a.FieldName == "LicenseStopTime" &&
|
||||
a.CultureName == LanguageCodes.En))
|
||||
{
|
||||
await _listFormFieldRepository.InsertAsync(new()
|
||||
{
|
||||
ListFormCode = existingListForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.DateTime,
|
||||
FieldName = "LicenseStopTime",
|
||||
CaptionName = "App.Listform.ListformField.LicenseStopTime",
|
||||
Width = 0,
|
||||
ListOrderNo = 23,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(existingListForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
});
|
||||
}
|
||||
|
||||
if (!await _listFormFieldRepository.AnyAsync(a =>
|
||||
a.ListFormCode == listFormName &&
|
||||
a.FieldName == "IsExisting" &&
|
||||
a.CultureName == LanguageCodes.En))
|
||||
{
|
||||
await _listFormFieldRepository.InsertAsync(new()
|
||||
{
|
||||
ListFormCode = existingListForm.ListFormCode,
|
||||
CultureName = LanguageCodes.En,
|
||||
SourceDbType = DbType.Boolean,
|
||||
FieldName = "IsExisting",
|
||||
CaptionName = "App.Listform.ListformField.IsExisting",
|
||||
Width = 0,
|
||||
ListOrderNo = 24,
|
||||
Visible = true,
|
||||
IsActive = true,
|
||||
|
||||
AllowSearch = true,
|
||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||
PermissionJson = DefaultFieldPermissionJson(existingListForm.Name),
|
||||
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Sales Order Item
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ public class Country : FullAuditedEntity<string>
|
|||
public string GroupName { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public int PhoneCode { get; set; }
|
||||
public int? PhoneNumberMinLength { get; set; }
|
||||
public int? PhoneNumberMaxLength { get; set; }
|
||||
public string? PhoneNumberFormat { get; set; }
|
||||
public string TaxLabel { get; set; }
|
||||
public bool ZipRequired { get; set; }
|
||||
public bool StateRequired { get; set; }
|
||||
|
|
@ -23,6 +26,9 @@ public class Country : FullAuditedEntity<string>
|
|||
string groupName,
|
||||
string currency,
|
||||
int phoneCode,
|
||||
int? phoneNumberMinLength,
|
||||
int? phoneNumberMaxLength,
|
||||
string? phoneNumberFormat,
|
||||
string taxLabel,
|
||||
bool zipRequired = false,
|
||||
bool stateRequired = false)
|
||||
|
|
@ -32,6 +38,9 @@ public class Country : FullAuditedEntity<string>
|
|||
GroupName = groupName;
|
||||
Currency = currency;
|
||||
PhoneCode = phoneCode;
|
||||
PhoneNumberMinLength = phoneNumberMinLength;
|
||||
PhoneNumberMaxLength = phoneNumberMaxLength;
|
||||
PhoneNumberFormat = phoneNumberFormat;
|
||||
TaxLabel = taxLabel;
|
||||
ZipRequired = zipRequired;
|
||||
StateRequired = stateRequired;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ public class Order : FullAuditedEntity<Guid>
|
|||
{
|
||||
public string Name { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool IsExisting { get; set; }
|
||||
|
||||
public string OrganizationName { get; set; }
|
||||
public string Founder { get; set; }
|
||||
|
|
@ -26,6 +27,11 @@ public class Order : FullAuditedEntity<Guid>
|
|||
public string Website { get; set; }
|
||||
public string MenuGroup { get; set; }
|
||||
|
||||
public string BillingCycle { get; set; }
|
||||
public int Period { get; set; }
|
||||
public DateTime? LicenseStartTime { get; set; }
|
||||
public DateTime? LicenseStopTime { get; set; }
|
||||
|
||||
public List<OrderItem> Items { get; set; } = new();
|
||||
public decimal Subtotal { get; set; }
|
||||
public decimal Commission { get; set; }
|
||||
|
|
|
|||
|
|
@ -747,6 +747,9 @@ public class PlatformDbContext :
|
|||
b.Property(x => x.GroupName).HasMaxLength(128);
|
||||
b.Property(x => x.TaxLabel).HasMaxLength(64);
|
||||
b.Property(x => x.Currency).IsRequired().HasMaxLength(8);
|
||||
b.Property(x => x.PhoneNumberMinLength).HasDefaultValue(0);
|
||||
b.Property(x => x.PhoneNumberMaxLength).HasDefaultValue(0);
|
||||
b.Property(x => x.PhoneNumberFormat).HasMaxLength(64);
|
||||
|
||||
b.HasIndex(x => new { x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
||||
|
||||
|
|
@ -1011,6 +1014,7 @@ public class PlatformDbContext :
|
|||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(o => o.Name).HasMaxLength(64);
|
||||
b.Property(o => o.IsExisting).IsRequired().HasDefaultValue(false);
|
||||
b.Property(o => o.OrganizationName).HasMaxLength(128);
|
||||
b.Property(o => o.Founder).HasMaxLength(128);
|
||||
b.Property(o => o.TaxOffice).HasMaxLength(128);
|
||||
|
|
@ -1026,6 +1030,8 @@ public class PlatformDbContext :
|
|||
b.Property(o => o.Email).HasMaxLength(128);
|
||||
b.Property(o => o.Website).HasMaxLength(128);
|
||||
b.Property(o => o.MenuGroup).HasMaxLength(64);
|
||||
b.Property(o => o.BillingCycle).HasMaxLength(32);
|
||||
b.Property(o => o.Period).IsRequired().HasDefaultValue(1);
|
||||
|
||||
b.Property(o => o.Subtotal).IsRequired().HasPrecision(18, 2);
|
||||
b.Property(o => o.Commission).IsRequired().HasPrecision(18, 2);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
|||
namespace Sozsoft.Platform.Migrations
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
[Migration("20260625183130_Initial")]
|
||||
[Migration("20260706213526_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -1542,6 +1542,20 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Property<int>("PhoneCode")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PhoneNumberFormat")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<int?>("PhoneNumberMaxLength")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<int?>("PhoneNumberMinLength")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<bool>("StateRequired")
|
||||
.HasColumnType("bit");
|
||||
|
||||
|
|
@ -4071,6 +4085,10 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<string>("BillingCycle")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("nvarchar(32)");
|
||||
|
||||
b.Property<string>("City")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
|
@ -4127,6 +4145,11 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<bool>("IsExisting")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
|
@ -4135,6 +4158,12 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<DateTime?>("LicenseStartTime")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime?>("LicenseStopTime")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("MenuGroup")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
|
@ -4158,6 +4187,11 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Property<Guid>("PaymentMethodId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<int>("Period")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(1);
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
|
@ -1573,6 +1573,7 @@ namespace Sozsoft.Platform.Migrations
|
|||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||
IsExisting = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
OrganizationName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
Founder = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
VknTckn = table.Column<long>(type: "bigint", nullable: true),
|
||||
|
|
@ -1589,6 +1590,10 @@ namespace Sozsoft.Platform.Migrations
|
|||
Email = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
Website = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
MenuGroup = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
BillingCycle = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
|
||||
Period = table.Column<int>(type: "int", nullable: false, defaultValue: 1),
|
||||
LicenseStartTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LicenseStopTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
Subtotal = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
Commission = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
Total = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
|
|
@ -2683,6 +2688,9 @@ namespace Sozsoft.Platform.Migrations
|
|||
GroupName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
Currency = table.Column<string>(type: "nvarchar(8)", maxLength: 8, nullable: false),
|
||||
PhoneCode = table.Column<int>(type: "int", nullable: false),
|
||||
PhoneNumberMinLength = table.Column<int>(type: "int", nullable: true, defaultValue: 0),
|
||||
PhoneNumberMaxLength = table.Column<int>(type: "int", nullable: true, defaultValue: 0),
|
||||
PhoneNumberFormat = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
TaxLabel = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
ZipRequired = table.Column<bool>(type: "bit", nullable: false),
|
||||
StateRequired = table.Column<bool>(type: "bit", nullable: false),
|
||||
|
|
@ -1539,6 +1539,20 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Property<int>("PhoneCode")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PhoneNumberFormat")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<int?>("PhoneNumberMaxLength")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<int?>("PhoneNumberMinLength")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<bool>("StateRequired")
|
||||
.HasColumnType("bit");
|
||||
|
||||
|
|
@ -4068,6 +4082,10 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<string>("BillingCycle")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("nvarchar(32)");
|
||||
|
||||
b.Property<string>("City")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
|
@ -4124,6 +4142,11 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<bool>("IsExisting")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
|
@ -4132,6 +4155,12 @@ namespace Sozsoft.Platform.Migrations
|
|||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<DateTime?>("LicenseStartTime")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime?>("LicenseStopTime")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("MenuGroup")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
|
@ -4155,6 +4184,11 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Property<Guid>("PaymentMethodId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<int>("Period")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(1);
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
|
|
|||
|
|
@ -15,11 +15,123 @@ import {
|
|||
FaUser,
|
||||
FaUserPlus,
|
||||
} from 'react-icons/fa'
|
||||
import React, { useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { Button } from '@/components/ui'
|
||||
import { Button, Select } from '@/components/ui'
|
||||
import {
|
||||
CityDto,
|
||||
CountryDto,
|
||||
DistrictDto,
|
||||
getCities,
|
||||
getCountries,
|
||||
getDistricts,
|
||||
} from '@/services/location.service'
|
||||
|
||||
interface LocationOption<T> {
|
||||
label: string
|
||||
value: string
|
||||
data: T
|
||||
}
|
||||
|
||||
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
const DEFAULT_PHONE_FORMAT = '000 000 000 000 000'
|
||||
const TURKEY_PHONE_FORMAT = '(000) 000-0000'
|
||||
const REQUIRED_NEW_CUSTOMER_FIELDS: Array<keyof CustomTenantDto> = [
|
||||
'organizationName',
|
||||
'founder',
|
||||
'country',
|
||||
'city',
|
||||
'district',
|
||||
'postalCode',
|
||||
'address1',
|
||||
'phoneNumber',
|
||||
'email',
|
||||
'taxOffice',
|
||||
'vknTckn',
|
||||
]
|
||||
const REQUIRED_EXISTING_CUSTOMER_FIELDS: Array<keyof CustomTenantDto> = [
|
||||
'id',
|
||||
'name',
|
||||
'organizationName',
|
||||
'founder',
|
||||
'country',
|
||||
'city',
|
||||
'district',
|
||||
'address1',
|
||||
'email',
|
||||
]
|
||||
|
||||
const getPhoneFormat = (country?: CountryDto) =>
|
||||
country?.phoneNumberFormat ||
|
||||
(country?.phoneCode === 90 ? TURKEY_PHONE_FORMAT : DEFAULT_PHONE_FORMAT)
|
||||
|
||||
const getPhoneMaxLength = (country?: CountryDto) =>
|
||||
country?.phoneNumberMaxLength ?? getPhoneFormat(country).replace(/[^0]/g, '').length
|
||||
|
||||
const getPhoneMinLength = (country?: CountryDto) =>
|
||||
country?.phoneNumberMinLength ?? getPhoneMaxLength(country)
|
||||
|
||||
const getNationalPhoneDigits = (value: string, country?: CountryDto) => {
|
||||
let digits = value.replace(/\D/g, '')
|
||||
if (!country) return digits
|
||||
|
||||
const code = String(country.phoneCode)
|
||||
if (value.trimStart().startsWith('+') && digits.startsWith(code)) {
|
||||
digits = digits.slice(code.length)
|
||||
}
|
||||
return digits
|
||||
}
|
||||
|
||||
const getPhoneValidationError = (value: string, country?: CountryDto) => {
|
||||
if (!country) return 'Lütfen önce ülke seçin.'
|
||||
|
||||
const length = getNationalPhoneDigits(value, country).length
|
||||
const minLength = getPhoneMinLength(country)
|
||||
const maxLength = getPhoneMaxLength(country)
|
||||
if (length >= minLength && length <= maxLength) return ''
|
||||
|
||||
return minLength === maxLength
|
||||
? `Telefon numarası ${minLength} haneli olmalıdır.`
|
||||
: `Telefon numarası ${minLength}-${maxLength} hane arasında olmalıdır.`
|
||||
}
|
||||
|
||||
const formatPhoneNumber = (value: string, country?: CountryDto) => {
|
||||
if (!country) return value.replace(/\D/g, '').slice(0, 15)
|
||||
|
||||
const maxLength = getPhoneMaxLength(country)
|
||||
const digits = getNationalPhoneDigits(value, country).slice(0, maxLength)
|
||||
const format = getPhoneFormat(country)
|
||||
let digitIndex = 0
|
||||
let nationalNumber = ''
|
||||
|
||||
for (const character of format) {
|
||||
if (character === '0') {
|
||||
if (digitIndex >= digits.length) break
|
||||
nationalNumber += digits[digitIndex]
|
||||
digitIndex += 1
|
||||
} else if (digitIndex === 0) {
|
||||
if (digits.length > 0) nationalNumber += character
|
||||
} else if (digitIndex < digits.length || character === ')') {
|
||||
nationalNumber += character
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return `+${country.phoneCode}${nationalNumber ? ` ${nationalNumber}` : ''}`
|
||||
}
|
||||
|
||||
const isBlankValue = (value: unknown) =>
|
||||
value === undefined || value === null || String(value).trim() === ''
|
||||
|
||||
const hasRequiredFields = (
|
||||
tenant: Partial<CustomTenantDto>,
|
||||
fields: Array<keyof CustomTenantDto>,
|
||||
) => fields.every((field) => !isBlankValue(tenant[field]))
|
||||
|
||||
const normalizeCode = (value?: string) => value?.trim().toLowerCase() || ''
|
||||
|
||||
interface TenantFormProps {
|
||||
onSubmit: (tenant: CustomTenantDto) => void
|
||||
|
|
@ -29,22 +141,203 @@ interface TenantFormProps {
|
|||
export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
||||
const [isExisting, setIsExisting] = useState<boolean>(true)
|
||||
const [formData, setFormData] = useState<Partial<CustomTenantDto>>({})
|
||||
const [countries, setCountries] = useState<CountryDto[]>([])
|
||||
const [cities, setCities] = useState<CityDto[]>([])
|
||||
const [districts, setDistricts] = useState<DistrictDto[]>([])
|
||||
const [isLoadingCountries, setIsLoadingCountries] = useState(false)
|
||||
const [isLoadingCities, setIsLoadingCities] = useState(false)
|
||||
const [isLoadingDistricts, setIsLoadingDistricts] = useState(false)
|
||||
const [isLoadingTenant, setIsLoadingTenant] = useState(false)
|
||||
const [emailError, setEmailError] = useState('')
|
||||
const [phoneError, setPhoneError] = useState('')
|
||||
const [existingTenantError, setExistingTenantError] = useState('')
|
||||
const [formError, setFormError] = useState('')
|
||||
const [foundTenantName, setFoundTenantName] = useState('')
|
||||
const navigate = useNavigate()
|
||||
const { translate } = useLocalization()
|
||||
|
||||
useEffect(() => {
|
||||
const loadCountries = async () => {
|
||||
setIsLoadingCountries(true)
|
||||
try {
|
||||
const response = await getCountries()
|
||||
setCountries(response.data)
|
||||
|
||||
const defaultCountry = response.data.find((country) => country.name === 'Türkiye')
|
||||
if (defaultCountry) {
|
||||
setFormData((prev) =>
|
||||
prev.country
|
||||
? prev
|
||||
: {
|
||||
...prev,
|
||||
country: defaultCountry.name,
|
||||
city: '',
|
||||
district: '',
|
||||
phoneNumber: `+${defaultCountry.phoneCode}`,
|
||||
},
|
||||
)
|
||||
|
||||
setIsLoadingCities(true)
|
||||
try {
|
||||
const cityResponse = await getCities(defaultCountry.name)
|
||||
setCities(cityResponse.data)
|
||||
} catch (error) {
|
||||
console.error('Şehirler alınırken hata:', error)
|
||||
} finally {
|
||||
setIsLoadingCities(false)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ülkeler alınırken hata:', error)
|
||||
} finally {
|
||||
setIsLoadingCountries(false)
|
||||
}
|
||||
}
|
||||
|
||||
void loadCountries()
|
||||
}, [])
|
||||
|
||||
const selectedCountry = countries.find((country) => country.name === formData.country)
|
||||
const phoneMask = selectedCountry
|
||||
? `+${selectedCountry.phoneCode} ${getPhoneFormat(selectedCountry)}`
|
||||
: DEFAULT_PHONE_FORMAT
|
||||
const canSubmitExistingTenant =
|
||||
Boolean(
|
||||
formData.id &&
|
||||
formData.organizationName &&
|
||||
normalizeCode(foundTenantName) === normalizeCode(formData.name),
|
||||
) && !isLoadingTenant
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
onSubmit({ ...formData, isExisting: isExisting! } as CustomTenantDto)
|
||||
let hasValidationError = false
|
||||
setFormError('')
|
||||
|
||||
if (isExisting) {
|
||||
if (!formData.name?.trim()) {
|
||||
setExistingTenantError('Lütfen önce organization code girin.')
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
!formData.id ||
|
||||
normalizeCode(foundTenantName) !== normalizeCode(formData.name) ||
|
||||
!formData.organizationName
|
||||
) {
|
||||
setExistingTenantError('Devam etmek için önce organization code ile kurum bilgisini bulun.')
|
||||
return
|
||||
}
|
||||
|
||||
if (!hasRequiredFields(formData, REQUIRED_EXISTING_CUSTOMER_FIELDS)) {
|
||||
setExistingTenantError('Kurum bilgileri eksik geldi. Lütfen kurum kodunu tekrar aratın.')
|
||||
return
|
||||
}
|
||||
|
||||
onSubmit({ ...formData, isExisting: true } as CustomTenantDto)
|
||||
return
|
||||
}
|
||||
|
||||
if (!hasRequiredFields(formData, REQUIRED_NEW_CUSTOMER_FIELDS)) {
|
||||
setFormError('Lütfen zorunlu alanları eksiksiz doldurun.')
|
||||
hasValidationError = true
|
||||
}
|
||||
|
||||
const nextPhoneError = getPhoneValidationError(formData.phoneNumber || '', selectedCountry)
|
||||
setPhoneError(nextPhoneError)
|
||||
if (nextPhoneError) {
|
||||
hasValidationError = true
|
||||
}
|
||||
|
||||
if (!isExisting && !EMAIL_PATTERN.test(formData.email || '')) {
|
||||
setEmailError('Lütfen geçerli bir e-posta adresi girin.')
|
||||
hasValidationError = true
|
||||
}
|
||||
|
||||
if (hasValidationError) return
|
||||
onSubmit({ ...formData, isExisting: false } as CustomTenantDto)
|
||||
}
|
||||
|
||||
const handleInputChange = (field: keyof CustomTenantDto, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }))
|
||||
}
|
||||
|
||||
const getTenantInfo = async () => {
|
||||
if (!formData.name) return
|
||||
const resetExistingTenantDetails = (name: string) => {
|
||||
setFoundTenantName('')
|
||||
setExistingTenantError('')
|
||||
setFormError('')
|
||||
setFormData({ name })
|
||||
}
|
||||
|
||||
const handleCountryChange = async (option: LocationOption<CountryDto> | null) => {
|
||||
const country = option?.data
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
country: country?.name || '',
|
||||
city: '',
|
||||
district: '',
|
||||
phoneNumber: country ? `+${country.phoneCode}` : '',
|
||||
}))
|
||||
setCities([])
|
||||
setDistricts([])
|
||||
setPhoneError('')
|
||||
|
||||
if (!country) return
|
||||
setIsLoadingCities(true)
|
||||
try {
|
||||
const tenant = await getTenantByNameDetail(formData.name)
|
||||
const response = await getCities(country.name)
|
||||
setCities(response.data)
|
||||
} catch (error) {
|
||||
console.error('Şehirler alınırken hata:', error)
|
||||
} finally {
|
||||
setIsLoadingCities(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCityChange = async (option: LocationOption<CityDto> | null) => {
|
||||
const city = option?.data
|
||||
setFormData((prev) => ({ ...prev, city: city?.name || '', district: '' }))
|
||||
setDistricts([])
|
||||
|
||||
if (!city || !formData.country) return
|
||||
setIsLoadingDistricts(true)
|
||||
try {
|
||||
const response = await getDistricts(formData.country, city.name)
|
||||
setDistricts(response.data)
|
||||
} catch (error) {
|
||||
console.error('İlçeler alınırken hata:', error)
|
||||
} finally {
|
||||
setIsLoadingDistricts(false)
|
||||
}
|
||||
}
|
||||
|
||||
const countryOptions: LocationOption<CountryDto>[] = countries.map((country) => ({
|
||||
label: country.name,
|
||||
value: country.name,
|
||||
data: country,
|
||||
}))
|
||||
const cityOptions: LocationOption<CityDto>[] = cities.map((city) => ({
|
||||
label: city.name,
|
||||
value: city.name,
|
||||
data: city,
|
||||
}))
|
||||
const districtOptions: LocationOption<DistrictDto>[] = districts.map((district) => ({
|
||||
label: district.name,
|
||||
value: district.name,
|
||||
data: district,
|
||||
}))
|
||||
|
||||
const getTenantInfo = async () => {
|
||||
const tenantName = formData.name?.trim()
|
||||
if (!tenantName) {
|
||||
setExistingTenantError('Lütfen önce organization code girin.')
|
||||
return
|
||||
}
|
||||
|
||||
setIsLoadingTenant(true)
|
||||
setExistingTenantError('')
|
||||
setFormError('')
|
||||
try {
|
||||
const tenant = await getTenantByNameDetail(tenantName)
|
||||
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
|
|
@ -68,8 +361,13 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
website: tenant.data.website,
|
||||
menuGroup: tenant.data.menuGroup,
|
||||
}))
|
||||
setFoundTenantName(tenant.data.name)
|
||||
} catch (error) {
|
||||
setFoundTenantName('')
|
||||
setExistingTenantError('Kurum bulunamadı. Lütfen organization code bilgisini kontrol edin.')
|
||||
console.error('Kurum bilgisi alınırken hata:', error)
|
||||
} finally {
|
||||
setIsLoadingTenant(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +437,7 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
autoFocus
|
||||
placeholder="Enter your organization code"
|
||||
value={formData.name || ''}
|
||||
onChange={(e) => handleInputChange('name', e.target.value)}
|
||||
onChange={(e) => resetExistingTenantDetails(e.target.value)}
|
||||
onKeyDown={async (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
|
|
@ -151,23 +449,36 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={isLoadingTenant || !formData.name?.trim()}
|
||||
onClick={getTenantInfo}
|
||||
icon={<FaSearch className="w-4 h-4" />}
|
||||
variant="solid"
|
||||
size="sm"
|
||||
className="!h-[40px] min-w-[180px] sm:mr-2 sm:rounded-r-lg sm:rounded-l-none [&>span]:gap-2"
|
||||
>
|
||||
{translate('::Public.products.tenantForm.searchOrg')}
|
||||
{isLoadingTenant
|
||||
? 'Aranıyor...'
|
||||
: translate('::Public.products.tenantForm.searchOrg')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{existingTenantError && (
|
||||
<p className="mt-2 text-sm text-red-600">{existingTenantError}</p>
|
||||
)}
|
||||
{!existingTenantError &&
|
||||
formData.name &&
|
||||
!canSubmitExistingTenant &&
|
||||
!isLoadingTenant && (
|
||||
<p className="mt-2 text-sm text-amber-600">
|
||||
Devam etmek için önce Find Organization ile kurum bilgilerini getirin.
|
||||
</p>
|
||||
)}
|
||||
|
||||
{formData.organizationName && (
|
||||
<div className="grid grid-cols-1 gap-y-3 text-sm text-gray-700 dark:text-gray-300 p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<FaUser className="w-4 h-4 text-gray-500" />
|
||||
<span className="font-medium">
|
||||
{translate('::LoginPanel.Profil')}
|
||||
</span>
|
||||
<span className="font-medium">{translate('::LoginPanel.Profil')}</span>
|
||||
<span>{formData.founder}</span>
|
||||
</div>
|
||||
|
||||
|
|
@ -189,18 +500,14 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
|
||||
<div className="flex items-center gap-2">
|
||||
<FaPhone className="w-4 h-4 text-gray-500" />
|
||||
<span className="font-medium">
|
||||
{translate('::blog.categories.mobile')}:
|
||||
</span>
|
||||
<span className="font-medium">{translate('::blog.categories.mobile')}:</span>
|
||||
<span>{formData.mobileNumber}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-2">
|
||||
<FaMapPin className="w-4 h-4 text-gray-500 mt-0.5" />
|
||||
<div>
|
||||
<span className="font-medium">
|
||||
{translate('::App.Address')}:
|
||||
</span>
|
||||
<span className="font-medium">{translate('::App.Address')}:</span>
|
||||
<div>{formData.address1}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -215,9 +522,7 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
|
||||
<div className="flex items-center gap-2">
|
||||
<FaGlobe className="w-4 h-4 text-gray-500" />
|
||||
<span className="font-medium">
|
||||
{translate('::Public.common.city')}:
|
||||
</span>
|
||||
<span className="font-medium">{translate('::Public.common.city')}:</span>
|
||||
<span>{formData.city}</span>
|
||||
</div>
|
||||
|
||||
|
|
@ -306,32 +611,76 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
|
||||
{translate('::Public.payment.customer.country')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<FaPhone className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="tel"
|
||||
<Select<LocationOption<CountryDto>>
|
||||
required
|
||||
placeholder="+90 (___) ___-____"
|
||||
value={formData.phoneNumber || ''}
|
||||
onChange={(e) => handleInputChange('phoneNumber', e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
isClearable
|
||||
isLoading={isLoadingCountries}
|
||||
options={countryOptions}
|
||||
value={
|
||||
countryOptions.find((option) => option.value === formData.country) || null
|
||||
}
|
||||
onChange={handleCountryChange}
|
||||
placeholder="Ülke seçin"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::Abp.Account.EmailAddress')}
|
||||
{translate('::Public.common.city')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<FaEnvelope className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="email"
|
||||
<Select<LocationOption<CityDto>>
|
||||
required
|
||||
placeholder="sample@email.com"
|
||||
value={formData.email || ''}
|
||||
onChange={(e) => handleInputChange('email', e.target.value)}
|
||||
isClearable
|
||||
isDisabled={!formData.country}
|
||||
isLoading={isLoadingCities}
|
||||
options={cityOptions}
|
||||
value={cityOptions.find((option) => option.value === formData.city) || null}
|
||||
onChange={handleCityChange}
|
||||
placeholder={formData.country ? 'Şehir seçin' : 'Önce ülke seçin'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::Public.payment.customer.district')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Select<LocationOption<DistrictDto>>
|
||||
required
|
||||
isClearable
|
||||
isDisabled={!formData.city}
|
||||
isLoading={isLoadingDistricts}
|
||||
options={districtOptions}
|
||||
value={
|
||||
districtOptions.find((option) => option.value === formData.district) ||
|
||||
null
|
||||
}
|
||||
onChange={(option) =>
|
||||
handleInputChange('district', option?.data.name || '')
|
||||
}
|
||||
placeholder={formData.city ? 'İlçe seçin' : 'Önce şehir seçin'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::Public.payment.customer.postalCode')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<FaMapPin className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
placeholder="34782"
|
||||
value={formData.postalCode || ''}
|
||||
onChange={(e) => handleInputChange('postalCode', e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -358,69 +707,87 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::Public.payment.customer.country')}
|
||||
{translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<FaGlobe className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<FaPhone className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Türkiye"
|
||||
value={formData.country || ''}
|
||||
onChange={(e) => handleInputChange('country', e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
type="tel"
|
||||
required
|
||||
inputMode="numeric"
|
||||
placeholder={phoneMask.replace(/0/g, '_')}
|
||||
aria-label={`Telefon numarası, format: ${phoneMask}`}
|
||||
value={formData.phoneNumber || ''}
|
||||
onChange={(e) => {
|
||||
handleInputChange(
|
||||
'phoneNumber',
|
||||
formatPhoneNumber(e.target.value, selectedCountry),
|
||||
)
|
||||
if (phoneError) setPhoneError('')
|
||||
}}
|
||||
onBlur={() =>
|
||||
setPhoneError(
|
||||
getPhoneValidationError(formData.phoneNumber || '', selectedCountry),
|
||||
)
|
||||
}
|
||||
aria-invalid={Boolean(phoneError)}
|
||||
aria-describedby={phoneError ? 'tenant-phone-error' : undefined}
|
||||
title={`Beklenen format: ${phoneMask}`}
|
||||
className={`w-full pl-10 pr-4 py-2 border rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
||||
phoneError
|
||||
? 'border-red-500 dark:border-red-500'
|
||||
: 'border-gray-300 dark:border-gray-700'
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
{phoneError && (
|
||||
<p id="tenant-phone-error" className="mt-1 text-sm text-red-600">
|
||||
{phoneError}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::Public.common.city')}
|
||||
{translate('::Abp.Account.EmailAddress')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<FaGlobe className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<FaEnvelope className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="text"
|
||||
type="email"
|
||||
required
|
||||
placeholder="İstanbul"
|
||||
value={formData.city || ''}
|
||||
onChange={(e) => handleInputChange('city', e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::Public.payment.customer.district')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<FaMapPin className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
placeholder="Çekmeköy"
|
||||
value={formData.district || ''}
|
||||
onChange={(e) => handleInputChange('district', e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::Public.payment.customer.postalCode')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<FaMapPin className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
placeholder="34782"
|
||||
value={formData.postalCode || ''}
|
||||
onChange={(e) => handleInputChange('postalCode', e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="sample@email.com"
|
||||
value={formData.email || ''}
|
||||
onChange={(e) => {
|
||||
handleInputChange('email', e.target.value)
|
||||
if (emailError) {
|
||||
setEmailError(
|
||||
EMAIL_PATTERN.test(e.target.value)
|
||||
? ''
|
||||
: 'Lütfen geçerli bir e-posta adresi girin.',
|
||||
)
|
||||
}
|
||||
}}
|
||||
onBlur={() =>
|
||||
setEmailError(
|
||||
EMAIL_PATTERN.test(formData.email || '')
|
||||
? ''
|
||||
: 'Lütfen geçerli bir e-posta adresi girin.',
|
||||
)
|
||||
}
|
||||
aria-invalid={Boolean(emailError)}
|
||||
aria-describedby={emailError ? 'tenant-email-error' : undefined}
|
||||
className={`w-full pl-10 pr-4 py-2 border rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
||||
emailError
|
||||
? 'border-red-500 dark:border-red-500'
|
||||
: 'border-gray-300 dark:border-gray-700'
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
{emailError && (
|
||||
<p id="tenant-email-error" className="mt-1 text-sm text-red-600">
|
||||
{emailError}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -477,6 +844,8 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{formError && <p className="mt-2 text-sm text-red-600">{formError}</p>}
|
||||
|
||||
<div className="flex justify-between items-center mt-6">
|
||||
<Button
|
||||
type="button"
|
||||
|
|
@ -489,6 +858,7 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isExisting && !canSubmitExistingTenant}
|
||||
icon={<FaArrowRight className="w-5 h-5" />}
|
||||
variant="solid"
|
||||
color="green-600"
|
||||
|
|
@ -503,4 +873,3 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@ export interface InstallmentOptionDto {
|
|||
export interface OrderDto {
|
||||
tenant: CustomTenantDto
|
||||
items: BasketItem[]
|
||||
billingCycle: BillingCycle
|
||||
period: number
|
||||
licenseStartTime?: string
|
||||
licenseStopTime?: string
|
||||
subtotal: number
|
||||
commission: number
|
||||
total: number
|
||||
|
|
|
|||
45
ui/src/services/location.service.ts
Normal file
45
ui/src/services/location.service.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import apiService from './api.service'
|
||||
|
||||
export interface CountryDto {
|
||||
id: string
|
||||
name: string
|
||||
phoneCode: number
|
||||
phoneNumberMinLength: number | null
|
||||
phoneNumberMaxLength: number | null
|
||||
phoneNumberFormat: string | null
|
||||
}
|
||||
|
||||
export interface CityDto {
|
||||
id: string
|
||||
country: string
|
||||
name: string
|
||||
plateCode?: string
|
||||
}
|
||||
|
||||
export interface DistrictDto {
|
||||
id: string
|
||||
country: string
|
||||
city: string
|
||||
name: string
|
||||
township?: string
|
||||
}
|
||||
|
||||
export const getCountries = () =>
|
||||
apiService.fetchData<CountryDto[]>({
|
||||
method: 'GET',
|
||||
url: '/api/app/public/country',
|
||||
})
|
||||
|
||||
export const getCities = (country: string) =>
|
||||
apiService.fetchData<CityDto[]>({
|
||||
method: 'GET',
|
||||
url: '/api/app/public/city',
|
||||
params: { country },
|
||||
})
|
||||
|
||||
export const getDistricts = (country: string, city: string) =>
|
||||
apiService.fetchData<DistrictDto[]>({
|
||||
method: 'GET',
|
||||
url: '/api/app/public/district',
|
||||
params: { country, city },
|
||||
})
|
||||
|
|
@ -44,6 +44,10 @@ export class OrderService {
|
|||
data: {
|
||||
tenant: orderData.tenant,
|
||||
items: orderData.items,
|
||||
billingCycle: orderData.billingCycle,
|
||||
period: orderData.period,
|
||||
licenseStartTime: orderData.licenseStartTime,
|
||||
licenseStopTime: orderData.licenseStopTime,
|
||||
subtotal: orderData.subtotal,
|
||||
commission: orderData.commission,
|
||||
total: orderData.total,
|
||||
|
|
|
|||
|
|
@ -62,13 +62,34 @@ const Payment: React.FC = () => {
|
|||
setCartState(clearCart())
|
||||
}
|
||||
|
||||
const calculateLicenseStopTime = (startTime: Date, billingCycle: string, period: number) => {
|
||||
const stopTime = new Date(startTime)
|
||||
|
||||
if (billingCycle === 'monthly') {
|
||||
stopTime.setMonth(stopTime.getMonth() + period)
|
||||
} else {
|
||||
stopTime.setFullYear(stopTime.getFullYear() + period)
|
||||
}
|
||||
|
||||
return stopTime
|
||||
}
|
||||
|
||||
const handlePaymentComplete = async (paymentData: Record<string, unknown>) => {
|
||||
if (!tenant) return
|
||||
|
||||
try {
|
||||
const billingCycle = cartState.globalBillingCycle || 'yearly'
|
||||
const period = cartState.globalPeriod > 0 ? cartState.globalPeriod : 1
|
||||
const licenseStartTime = new Date()
|
||||
const licenseStopTime = calculateLicenseStopTime(licenseStartTime, billingCycle, period)
|
||||
|
||||
const orderData = {
|
||||
tenant,
|
||||
items: cartState.items,
|
||||
billingCycle,
|
||||
period,
|
||||
licenseStartTime: licenseStartTime.toISOString(),
|
||||
licenseStopTime: licenseStopTime.toISOString(),
|
||||
subtotal: cartState.total,
|
||||
commission: paymentData.commission,
|
||||
total: paymentData.total,
|
||||
|
|
@ -136,20 +157,20 @@ const Payment: React.FC = () => {
|
|||
|
||||
<div className="container mx-auto px-4 pt-4">
|
||||
<PaymentForm
|
||||
cartState={cartState}
|
||||
tenant={tenant}
|
||||
onBack={handleBackToTenant}
|
||||
onComplete={handlePaymentComplete}
|
||||
cartState={cartState}
|
||||
/>
|
||||
|
||||
<Cart
|
||||
cartState={cartState}
|
||||
isOpen={isCartOpen}
|
||||
clearCart={handleClearCart}
|
||||
removeItem={handleRemoveItem}
|
||||
updateQuantity={handleUpdateQuantity}
|
||||
onClose={() => setIsCartOpen(false)}
|
||||
onCheckout={handleCheckout}
|
||||
cartState={cartState}
|
||||
updateQuantity={handleUpdateQuantity}
|
||||
removeItem={handleRemoveItem}
|
||||
clearCart={handleClearCart}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue