diff --git a/src/app.module.ts b/src/app.module.ts index a40a9e5..fafa45c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -16,6 +16,7 @@ import { StylistsModule } from './stylists/stylists.module'; import { SubscriptionsModule } from './subscriptions/subscriptions.module'; import { UsersModule } from './users/users.module'; import { PaymentsModule } from './payments/payments.module'; +import { ProvincesModule } from './provinces/provinces.module'; import { UploaderModule } from './uploader/uploader.module'; @Module({ @@ -38,6 +39,7 @@ import { UploaderModule } from './uploader/uploader.module'; SentMessagesModule, SubscriptionsModule, PaymentsModule, + ProvincesModule, UploaderModule, ], controllers: [], diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 4058e8f..18e1db7 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -7,6 +7,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { AuthorizationModule } from '../authorization/authorization.module'; import { Customer } from '../customers/entities/customer.entity'; import { Salon } from '../salons/entities/salon.entity'; +import { ProvincesModule } from '../provinces/provinces.module'; import { SmsModule } from '../sms/sms.module'; import { SubscriptionsModule } from '../subscriptions/subscriptions.module'; import { UsersModule } from '../users/users.module'; @@ -36,6 +37,7 @@ import { JwtStrategy } from './strategies/jwt.strategy'; SmsModule, SubscriptionsModule, AuthorizationModule, + ProvincesModule, ], controllers: [AuthController], providers: [ diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index bb07295..61e96cc 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -14,6 +14,7 @@ import * as bcrypt from 'bcrypt'; import { Repository, IsNull } from 'typeorm'; import { Customer } from '../customers/entities/customer.entity'; import { Salon } from '../salons/entities/salon.entity'; +import { ProvincesService } from '../provinces/provinces.service'; import { SmsService } from '../sms/sms.service'; import { SalonSubscription } from '../subscriptions/entities/salon-subscription.entity'; import { SalonSubscriptionsService } from '../subscriptions/salon-subscriptions.service'; @@ -69,6 +70,7 @@ export class AuthService { private readonly customersRepository: Repository, @InjectRepository(Salon) private readonly salonsRepository: Repository, + private readonly provincesService: ProvincesService, private readonly usersService: UsersService, private readonly salonSubscriptionsService: SalonSubscriptionsService, private readonly smsService: SmsService, @@ -274,6 +276,7 @@ export class AuthService { dto: VerifySalonOtpDto, ): Promise { this.validateSalonSignupFields(dto); + await this.validateSalonLocation(dto.salonProvinceId!, dto.salonCityId!); const user = await this.usersService.create({ firstName: dto.firstName!, @@ -287,6 +290,8 @@ export class AuthService { description: dto.salonDescription ?? null, address: dto.salonAddress!, phone: dto.salonPhone!, + provinceId: dto.salonProvinceId!, + cityId: dto.salonCityId!, ownerId: user.id, }); @@ -301,6 +306,8 @@ export class AuthService { 'salonName', 'salonAddress', 'salonPhone', + 'salonProvinceId', + 'salonCityId', ]; const missingFields = requiredFields.filter((field) => !dto[field]); @@ -312,6 +319,16 @@ export class AuthService { } } + private async validateSalonLocation( + provinceId: string, + cityId: string, + ): Promise { + const city = await this.provincesService.findCity(provinceId, cityId); + if (!city) { + throw new BadRequestException('شهر انتخاب‌شده با استان مطابقت ندارد'); + } + } + async verifyOtp(dto: VerifyOtpDto): Promise { await this.consumeOtp(dto.mobile, dto.code); @@ -390,6 +407,7 @@ export class AuthService { if (user.role === UserRole.SALON) { response.salons = await this.salonsRepository.find({ where: { ownerId: user.id }, + relations: { province: true, city: true }, order: { createdAt: 'ASC' }, }); response.activeSubscription = diff --git a/src/auth/dto/verify-salon-otp.dto.ts b/src/auth/dto/verify-salon-otp.dto.ts index 06751c0..65101d4 100644 --- a/src/auth/dto/verify-salon-otp.dto.ts +++ b/src/auth/dto/verify-salon-otp.dto.ts @@ -1,6 +1,7 @@ import { IsOptional, IsString, + IsUUID, Length, Matches, } from 'class-validator'; @@ -40,4 +41,12 @@ export class VerifySalonOtpDto { @IsOptional() @Matches(/^09\d{9}$/) salonPhone?: string; + + @IsOptional() + @IsUUID() + salonProvinceId?: string; + + @IsOptional() + @IsUUID() + salonCityId?: string; } diff --git a/src/authorization/authorization.module.ts b/src/authorization/authorization.module.ts index f9526c8..f1bb0d5 100644 --- a/src/authorization/authorization.module.ts +++ b/src/authorization/authorization.module.ts @@ -5,6 +5,7 @@ import { AuthorizationService } from './authorization.service'; import { Permission } from './entities/permission.entity'; import { UserPermission } from './entities/user-permission.entity'; import { CustomerPolicy } from './policies/customer.policy'; +import { ProvincePolicy } from './policies/province.policy'; import { ReservePolicy } from './policies/reserve.policy'; import { SalonPolicy } from './policies/salon.policy'; import { ServicePolicy } from './policies/service.policy'; @@ -17,6 +18,7 @@ import { SubscriptionPolicy } from './policies/subscription.policy'; AuthorizationService, SalonPolicy, ServicePolicy, + ProvincePolicy, CustomerPolicy, StylistPolicy, ReservePolicy, @@ -26,6 +28,7 @@ import { SubscriptionPolicy } from './policies/subscription.policy'; AuthorizationService, SalonPolicy, ServicePolicy, + ProvincePolicy, CustomerPolicy, StylistPolicy, ReservePolicy, diff --git a/src/authorization/enums/permission-code.enum.ts b/src/authorization/enums/permission-code.enum.ts index fd2efbc..ecc3258 100644 --- a/src/authorization/enums/permission-code.enum.ts +++ b/src/authorization/enums/permission-code.enum.ts @@ -16,6 +16,9 @@ export enum PermissionCode { SERVICES_READ = 'services:read', SERVICES_WRITE = 'services:write', SERVICES_DELETE = 'services:delete', + PROVINCES_READ = 'provinces:read', + PROVINCES_WRITE = 'provinces:write', + PROVINCES_DELETE = 'provinces:delete', RESERVES_READ = 'reserves:read', RESERVES_WRITE = 'reserves:write', SUBSCRIPTIONS_READ = 'subscriptions:read', diff --git a/src/authorization/policies/province.policy.ts b/src/authorization/policies/province.policy.ts new file mode 100644 index 0000000..b1b7328 --- /dev/null +++ b/src/authorization/policies/province.policy.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; +import { AuthUser } from '../../auth/interfaces/auth-user.interface'; +import { AuthorizationService } from '../authorization.service'; + +@Injectable() +export class ProvincePolicy { + constructor(private readonly authorizationService: AuthorizationService) {} + + canManage(user: AuthUser): boolean { + return this.authorizationService.isAdmin(user); + } +} diff --git a/src/database/database.module.ts b/src/database/database.module.ts index 45688ea..7f7e47c 100644 --- a/src/database/database.module.ts +++ b/src/database/database.module.ts @@ -1,11 +1,13 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Customer } from '../customers/entities/customer.entity'; +import { City } from '../provinces/entities/city.entity'; +import { Province } from '../provinces/entities/province.entity'; import { User } from '../users/entities/user.entity'; import { SeedService } from './seed.service'; @Module({ - imports: [TypeOrmModule.forFeature([User, Customer])], + imports: [TypeOrmModule.forFeature([User, Customer, Province, City])], providers: [SeedService], }) export class DatabaseModule {} diff --git a/src/database/migrations/1721000000000-AddProvincesAndCities.ts b/src/database/migrations/1721000000000-AddProvincesAndCities.ts new file mode 100644 index 0000000..d7363c0 --- /dev/null +++ b/src/database/migrations/1721000000000-AddProvincesAndCities.ts @@ -0,0 +1,111 @@ +import { + MigrationInterface, + QueryRunner, + Table, + TableForeignKey, +} from 'typeorm'; + +export class AddProvincesAndCities1721000000000 implements MigrationInterface { + name = 'AddProvincesAndCities1721000000000'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable( + new Table({ + name: 'provinces', + columns: [ + { + name: 'id', + type: 'uuid', + isPrimary: true, + generationStrategy: 'uuid', + default: 'gen_random_uuid()', + }, + { name: 'name', type: 'varchar', length: '100', isUnique: true }, + { name: 'slug', type: 'varchar', length: '100', isUnique: true }, + { name: 'createdAt', type: 'timestamp', default: 'now()' }, + { name: 'updatedAt', type: 'timestamp', default: 'now()' }, + ], + }), + true, + ); + + await queryRunner.createTable( + new Table({ + name: 'cities', + columns: [ + { + name: 'id', + type: 'uuid', + isPrimary: true, + generationStrategy: 'uuid', + default: 'gen_random_uuid()', + }, + { name: 'provinceId', type: 'uuid' }, + { name: 'name', type: 'varchar', length: '100' }, + { name: 'slug', type: 'varchar', length: '100', isUnique: true }, + { name: 'createdAt', type: 'timestamp', default: 'now()' }, + { name: 'updatedAt', type: 'timestamp', default: 'now()' }, + ], + }), + true, + ); + + await queryRunner.createForeignKey( + 'cities', + new TableForeignKey({ + columnNames: ['provinceId'], + referencedTableName: 'provinces', + referencedColumnNames: ['id'], + onDelete: 'CASCADE', + }), + ); + + await queryRunner.query(` + ALTER TABLE "salons" + ADD COLUMN "provinceId" uuid, + ADD COLUMN "cityId" uuid + `); + + await queryRunner.createForeignKey( + 'salons', + new TableForeignKey({ + columnNames: ['provinceId'], + referencedTableName: 'provinces', + referencedColumnNames: ['id'], + onDelete: 'SET NULL', + }), + ); + + await queryRunner.createForeignKey( + 'salons', + new TableForeignKey({ + columnNames: ['cityId'], + referencedTableName: 'cities', + referencedColumnNames: ['id'], + onDelete: 'SET NULL', + }), + ); + } + + public async down(queryRunner: QueryRunner): Promise { + const salonsTable = await queryRunner.getTable('salons'); + if (salonsTable) { + for (const fk of salonsTable.foreignKeys.filter( + (key) => + key.columnNames.includes('provinceId') || + key.columnNames.includes('cityId'), + )) { + await queryRunner.dropForeignKey('salons', fk); + } + } + + await queryRunner.query(` + ALTER TABLE "salons" + DROP COLUMN IF EXISTS "provinceId", + DROP COLUMN IF EXISTS "cityId" + `); + + await queryRunner.dropTable('cities', true); + await queryRunner.dropTable('provinces', true); + } +} diff --git a/src/database/seed.service.ts b/src/database/seed.service.ts index e52672a..3f7c4e7 100644 --- a/src/database/seed.service.ts +++ b/src/database/seed.service.ts @@ -1,10 +1,34 @@ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; +import { City } from '../provinces/entities/city.entity'; +import { Province } from '../provinces/entities/province.entity'; import { Customer } from '../customers/entities/customer.entity'; import { User } from '../users/entities/user.entity'; import { UserRole } from '../users/enums/user-role.enum'; import { ADMIN_USERS_SEED } from './seeds/admin-users.seed'; +import iranCitiesSeed from './seeds/data/iran-cities.json'; +import iranProvincesSeed from './seeds/data/iran-provinces.json'; + +type IranProvinceSeed = { + id: number; + title: string; + slug: string; +}; + +type IranCitySeed = { + id: number; + title: string; + slug: string; + province_id: number; +}; + +const slugify = (value: string) => + value + .toLowerCase() + .trim() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-|-$/g, ''); @Injectable() export class SeedService implements OnModuleInit { @@ -15,10 +39,15 @@ export class SeedService implements OnModuleInit { private readonly usersRepository: Repository, @InjectRepository(Customer) private readonly customersRepository: Repository, + @InjectRepository(Province) + private readonly provincesRepository: Repository, + @InjectRepository(City) + private readonly citiesRepository: Repository, ) {} async onModuleInit(): Promise { await this.seedAdminUsers(); + await this.seedIranLocations(); } private async seedAdminUsers(): Promise { @@ -55,4 +84,45 @@ export class SeedService implements OnModuleInit { } } } + + private async seedIranLocations(): Promise { + const existingCount = await this.provincesRepository.count(); + if (existingCount > 0) { + return; + } + + const provinceIdMap = new Map(); + const usedCitySlugs = new Set(); + + for (const provinceSeed of iranProvincesSeed as IranProvinceSeed[]) { + const province = await this.provincesRepository.save({ + name: provinceSeed.title.trim(), + slug: slugify(provinceSeed.slug || provinceSeed.title), + }); + provinceIdMap.set(provinceSeed.id, province.id); + } + + for (const citySeed of iranCitiesSeed as IranCitySeed[]) { + const provinceId = provinceIdMap.get(citySeed.province_id); + if (!provinceId) { + continue; + } + + let slug = slugify(citySeed.slug || citySeed.title); + if (usedCitySlugs.has(slug)) { + slug = `${slug}-${citySeed.id}`; + } + usedCitySlugs.add(slug); + + await this.citiesRepository.save({ + provinceId, + name: citySeed.title.trim(), + slug, + }); + } + + this.logger.log( + `Seeded ${provinceIdMap.size} provinces and ${usedCitySlugs.size} cities`, + ); + } } diff --git a/src/database/seeds/data/iran-cities.json b/src/database/seeds/data/iran-cities.json new file mode 100644 index 0000000..9a48a09 --- /dev/null +++ b/src/database/seeds/data/iran-cities.json @@ -0,0 +1,2898 @@ +[ + { + "id": 102, + "title": "آبادان", + "slug": "Abadan", + "province_id": 7, + "latitude": 32.9925, + "longitude": 47.419722 + }, + { + "id": 129, + "title": "آباده", + "slug": "Abade", + "province_id": 8, + "latitude": 30.347296, + "longitude": 48.2934004 + }, + { + "id": 296, + "title": "آبدانان", + "slug": "ABDANAN", + "province_id": 17, + "latitude": 32.9925, + "longitude": 47.419722 + }, + { + "id": 65, + "title": "آذرشهر", + "slug": "Azarshahr", + "province_id": 4, + "latitude": 37.758889, + "longitude": 45.978333 + }, + { + "id": 332, + "title": "آرادان", + "slug": "Aradan", + "province_id": 21, + "latitude": 33.860278, + "longitude": 58.521667 + }, + { + "id": 226, + "title": "آران وبيدگل", + "slug": "Aran va Bidgol", + "province_id": 11, + "latitude": 34.057778, + "longitude": 51.484167 + }, + { + "id": 400, + "title": "آزادشهر", + "slug": "Azadshahr", + "province_id": 28, + "latitude": 37.758889, + "longitude": 45.978333 + }, + { + "id": 13, + "title": "آستارا", + "slug": "Astara", + "province_id": 2, + "latitude": 38.429167, + "longitude": 48.871944 + }, + { + "id": 14, + "title": "آستانه اشرفيه", + "slug": "Astaneh", + "province_id": 2, + "latitude": 37.2598022, + "longitude": 49.9436621 + }, + { + "id": 2, + "title": "آشتيان", + "slug": "Ashtian", + "province_id": 1, + "latitude": 34.521944, + "longitude": 50.006111 + }, + { + "id": 398, + "title": "آق قلا", + "slug": "Aqqala", + "province_id": 28, + "latitude": 37.013889, + "longitude": 54.455 + }, + { + "id": 29, + "title": "آمل", + "slug": "Amol", + "province_id": 3, + "latitude": 36.469722, + "longitude": 52.350833 + }, + { + "id": 341, + "title": "ابركوه", + "slug": "Abarkouh", + "province_id": 22, + "latitude": 31.1304036, + "longitude": 53.2503736 + }, + { + "id": 319, + "title": "ابهر", + "slug": "Abhar", + "province_id": 20, + "latitude": 38.4894305, + "longitude": 47.0683575 + }, + { + "id": 345, + "title": "ابوموسي", + "slug": "Abu Musa", + "province_id": 23, + "latitude": 25.8797106, + "longitude": 55.0328017 + }, + { + "id": 1, + "title": "اراک", + "slug": "Arak", + "province_id": 1, + "latitude": 34.091667, + "longitude": 49.689167 + }, + { + "id": 374, + "title": "اردبيل", + "slug": "Ardabil", + "province_id": 25, + "latitude": 38.4853276, + "longitude": 47.8911209 + }, + { + "id": 209, + "title": "اردستان", + "slug": "Ardestān", + "province_id": 11, + "latitude": 33.376111, + "longitude": 52.369444 + }, + { + "id": 335, + "title": "اردكان", + "slug": "Ardakan", + "province_id": 22, + "latitude": 33.376111, + "longitude": 52.369444 + }, + { + "id": 275, + "title": "اردل", + "slug": "Ardal", + "province_id": 15, + "latitude": 38.4853276, + "longitude": 47.8911209 + }, + { + "id": 145, + "title": "ارسنجان", + "slug": "Arsanjan", + "province_id": 8, + "latitude": 33.376111, + "longitude": 52.369444 + }, + { + "id": 71, + "title": "اروميه", + "slug": "Address:", + "province_id": 5, + "latitude": 37.555278, + "longitude": 45.0725 + }, + { + "id": 286, + "title": "ازنا", + "slug": "AZNA", + "province_id": 16, + "latitude": 33.455833, + "longitude": 49.455556 + }, + { + "id": 130, + "title": "استهبان", + "slug": "Estahban", + "province_id": 8, + "latitude": 35.696111, + "longitude": 51.423056 + }, + { + "id": 267, + "title": "اسدآباد", + "slug": "Asadabad", + "province_id": 14, + "latitude": 37.85, + "longitude": 46.833333 + }, + { + "id": 405, + "title": "اسفراين", + "slug": "Esfaraien", + "province_id": 29, + "latitude": 37.076389, + "longitude": 57.51 + }, + { + "id": 88, + "title": "اسلام آبادغرب", + "slug": "Islamic garb", + "province_id": 6, + "latitude": 33.7293882, + "longitude": 73.0931461 + }, + { + "id": 364, + "title": "اسلامشهر", + "slug": "Eslamshahr", + "province_id": 24, + "latitude": 35.5445805, + "longitude": 51.2302457 + }, + { + "id": 66, + "title": "اسکو", + "slug": "Osku", + "province_id": 4, + "latitude": 37.915833, + "longitude": 46.123611 + }, + { + "id": 83, + "title": "اشنويه", + "slug": "Oshnavieh", + "province_id": 5, + "latitude": 37.039722, + "longitude": 45.098333 + }, + { + "id": 210, + "title": "اصفهان", + "slug": "Esfahan", + "province_id": 11, + "latitude": 32.6546275, + "longitude": 51.6679826 + }, + { + "id": 131, + "title": "اقليد", + "slug": "Eghlid", + "province_id": 8, + "latitude": 30.898889, + "longitude": 52.686667 + }, + { + "id": 389, + "title": "البرز", + "slug": "Alborz", + "province_id": 27, + "latitude": 35.9960467, + "longitude": 50.9289246 + }, + { + "id": 280, + "title": "اليگودرز", + "slug": "Aligoudarz", + "province_id": 16, + "latitude": 33.400556, + "longitude": 49.695 + }, + { + "id": 25, + "title": "املش", + "slug": "Amlash", + "province_id": 2, + "latitude": 37.0916334, + "longitude": 50.1869377 + }, + { + "id": 177, + "title": "انار", + "slug": "Pomegranate", + "province_id": 9, + "latitude": 37.340278, + "longitude": 46.056111 + }, + { + "id": 103, + "title": "انديمشک", + "slug": "ANDIMESHK", + "province_id": 7, + "latitude": 32.46, + "longitude": 48.359167 + }, + { + "id": 51, + "title": "اهر", + "slug": "Ahar", + "province_id": 4, + "latitude": 38.4894305, + "longitude": 47.0683575 + }, + { + "id": 104, + "title": "اهواز", + "slug": "Ahvaz", + "province_id": 7, + "latitude": 31.3183272, + "longitude": 48.6706187 + }, + { + "id": 322, + "title": "ايجرود", + "slug": "Ijroud", + "province_id": 20, + "latitude": 36.4160928, + "longitude": 48.2469249 + }, + { + "id": 105, + "title": "ايذه", + "slug": "IZEH", + "province_id": 7, + "latitude": 31.834167, + "longitude": 49.867222 + }, + { + "id": 233, + "title": "ايرانشهر", + "slug": "Iranshahr", + "province_id": 12, + "latitude": 36.694444, + "longitude": 45.141667 + }, + { + "id": 291, + "title": "ايلام", + "slug": "Ilam", + "province_id": 17, + "latitude": 33.2957618, + "longitude": 46.670534 + }, + { + "id": 297, + "title": "ايوان", + "slug": "Ivan", + "province_id": 17, + "latitude": 33.2729171, + "longitude": 52.1985314 + }, + { + "id": 30, + "title": "بابل", + "slug": "Babol", + "province_id": 3, + "latitude": 36.7025, + "longitude": 52.6575 + }, + { + "id": 39, + "title": "بابلسر", + "slug": "Babolsar", + "province_id": 3, + "latitude": 32.468191, + "longitude": 44.5501935 + }, + { + "id": 307, + "title": "باشت", + "slug": "Basht", + "province_id": 18, + "latitude": 29.233056, + "longitude": 56.602222 + }, + { + "id": 116, + "title": "باغ ملک", + "slug": "Garden Property", + "province_id": 7, + "latitude": 32.3947206, + "longitude": 51.5965328 + }, + { + "id": 158, + "title": "بافت", + "slug": "Tissue", + "province_id": 9, + "latitude": 29.233056, + "longitude": 56.602222 + }, + { + "id": 336, + "title": "بافق", + "slug": "Bafg", + "province_id": 22, + "latitude": 35.9985999, + "longitude": 45.8823428 + }, + { + "id": 252, + "title": "بانه", + "slug": "Bane", + "province_id": 13, + "latitude": 35.2747322, + "longitude": 59.4677727 + }, + { + "id": 125, + "title": "باوي", + "slug": "Bavi", + "province_id": 7, + "latitude": 35.2747322, + "longitude": 59.4677727 + }, + { + "id": 200, + "title": "بجستان", + "slug": "Bajestan", + "province_id": 10, + "latitude": 33.376111, + "longitude": 52.369444 + }, + { + "id": 406, + "title": "بجنورد", + "slug": "Bojnoord", + "province_id": 29, + "latitude": 37.475, + "longitude": 57.333333 + }, + { + "id": 230, + "title": "برخوار", + "slug": "Borkhar", + "province_id": 11, + "latitude": 34.974722, + "longitude": 59.623611 + }, + { + "id": 195, + "title": "بردسكن", + "slug": "Bardaskan", + "province_id": 10, + "latitude": 35.260833, + "longitude": 57.969722 + }, + { + "id": 167, + "title": "بردسير", + "slug": "Bardsir", + "province_id": 9, + "latitude": 29.9275, + "longitude": 56.572222 + }, + { + "id": 281, + "title": "بروجرد", + "slug": "Borujerd", + "province_id": 16, + "latitude": 37.475, + "longitude": 57.333333 + }, + { + "id": 271, + "title": "بروجن", + "slug": "Borujen", + "province_id": 15, + "latitude": 31.965278, + "longitude": 51.287222 + }, + { + "id": 59, + "title": "بستان آباد", + "slug": "Bostanabad", + "province_id": 4, + "latitude": 37.85, + "longitude": 46.833333 + }, + { + "id": 353, + "title": "بستك", + "slug": "Block", + "province_id": 23, + "latitude": 27.199167, + "longitude": 54.366667 + }, + { + "id": 159, + "title": "بم", + "slug": "Bass", + "province_id": 9, + "latitude": 29.106111, + "longitude": 58.356944 + }, + { + "id": 58, + "title": "بناب", + "slug": "BONAB", + "province_id": 4, + "latitude": 37.340278, + "longitude": 46.056111 + }, + { + "id": 15, + "title": "بندرانزلي", + "slug": "Bandar Anzali", + "province_id": 2, + "latitude": 37.4724467, + "longitude": 49.4587312 + }, + { + "id": 346, + "title": "بندرعباس", + "slug": "Bandar Abbas", + "province_id": 23, + "latitude": 35.952222, + "longitude": 50.6075 + }, + { + "id": 347, + "title": "بندرلنگه", + "slug": "Lengeh seaport", + "province_id": 23, + "latitude": 26.558056, + "longitude": 54.880556 + }, + { + "id": 106, + "title": "بندرماهشهر", + "slug": "Bandar-e Mahshahr", + "province_id": 7, + "latitude": 33.139722, + "longitude": 47.376111 + }, + { + "id": 391, + "title": "بندرگز", + "slug": "Gaz", + "province_id": 28, + "latitude": 37.444444, + "longitude": 59.108056 + }, + { + "id": 344, + "title": "بهاباد", + "slug": "Bahabad", + "province_id": 22, + "latitude": 36.763056, + "longitude": 45.722222 + }, + { + "id": 268, + "title": "بهار", + "slug": "Spring", + "province_id": 14, + "latitude": 37.340278, + "longitude": 46.056111 + }, + { + "id": 371, + "title": "بهارستان", + "slug": "BAHARESTAN", + "province_id": 24, + "latitude": 33.376111, + "longitude": 52.369444 + }, + { + "id": 107, + "title": "بهبهان", + "slug": "PA", + "province_id": 7, + "latitude": 30.595833, + "longitude": 50.241667 + }, + { + "id": 31, + "title": "بهشهر", + "slug": "Behshahr", + "province_id": 3, + "latitude": 34.9083252, + "longitude": 48.4392729 + }, + { + "id": 305, + "title": "بهمئي", + "slug": "Bahmaie", + "province_id": 18, + "latitude": 0, + "longitude": 0 + }, + { + "id": 385, + "title": "بوئين زهرا", + "slug": "Buin Zahra", + "province_id": 27, + "latitude": 35.766944, + "longitude": 50.057778 + }, + { + "id": 144, + "title": "بوانات", + "slug": "Bavanat", + "province_id": 8, + "latitude": 33.220556, + "longitude": 50.315 + }, + { + "id": 309, + "title": "بوشهر", + "slug": "Bushehr", + "province_id": 19, + "latitude": 28.9233837, + "longitude": 50.820314 + }, + { + "id": 80, + "title": "بوكان", + "slug": "Bukan", + "province_id": 5, + "latitude": 36.521111, + "longitude": 46.208889 + }, + { + "id": 301, + "title": "بويراحمد", + "slug": "Boyer", + "province_id": 18, + "latitude": 30.724586, + "longitude": 50.8456323 + }, + { + "id": 253, + "title": "بيجار", + "slug": "Bijar", + "province_id": 13, + "latitude": 32.735278, + "longitude": 59.466667 + }, + { + "id": 413, + "title": "بيرجند", + "slug": "Birjand", + "province_id": 30, + "latitude": 32.8649039, + "longitude": 59.2262472 + }, + { + "id": 375, + "title": "بيله سوار", + "slug": "Bilesavar", + "province_id": 25, + "latitude": 39.3567775, + "longitude": 47.9490765 + }, + { + "id": 386, + "title": "تاكستان", + "slug": "The vineyard", + "province_id": 27, + "latitude": 28.9837547, + "longitude": 50.8330708 + }, + { + "id": 181, + "title": "تايباد", + "slug": "Taybad", + "province_id": 10, + "latitude": 34.74, + "longitude": 60.775556 + }, + { + "id": 52, + "title": "تبريز", + "slug": "Tabriz", + "province_id": 4, + "latitude": 38.066667, + "longitude": 46.3 + }, + { + "id": 183, + "title": "تربت جام", + "slug": "Torbat-e Jam", + "province_id": 10, + "latitude": 35.243889, + "longitude": 60.6225 + }, + { + "id": 182, + "title": "تربت حيدريه", + "slug": "Torbat", + "province_id": 10, + "latitude": 35.273889, + "longitude": 59.219444 + }, + { + "id": 337, + "title": "تفت", + "slug": "Taft", + "province_id": 22, + "latitude": 39.630631, + "longitude": -78.929542 + }, + { + "id": 3, + "title": "تفرش", + "slug": "Tafresh", + "province_id": 1, + "latitude": 34.691944, + "longitude": 50.013056 + }, + { + "id": 82, + "title": "تكاب", + "slug": "Tekab", + "province_id": 5, + "latitude": 37.340278, + "longitude": 46.056111 + }, + { + "id": 32, + "title": "تنكابن", + "slug": "Tonkabon", + "province_id": 3, + "latitude": 36.816389, + "longitude": 50.873889 + }, + { + "id": 310, + "title": "تنگستان", + "slug": "Tangistan", + "province_id": 19, + "latitude": 28.9837547, + "longitude": 50.8330708 + }, + { + "id": 358, + "title": "تهران", + "slug": "Tehran", + "province_id": 24, + "latitude": 31.94, + "longitude": 51.647778 + }, + { + "id": 262, + "title": "تويسركان", + "slug": "Tuyserkan", + "province_id": 14, + "latitude": 34.548056, + "longitude": 48.446944 + }, + { + "id": 227, + "title": "تيران وکرون", + "slug": "Tiran Vkrvn", + "province_id": 11, + "latitude": 0, + "longitude": 0 + }, + { + "id": 99, + "title": "ثلاث باباجاني", + "slug": "Salas Babajani", + "province_id": 6, + "latitude": 34.7358371, + "longitude": 46.1493969 + }, + { + "id": 407, + "title": "جاجرم", + "slug": "Jajarm", + "province_id": 29, + "latitude": 36.95, + "longitude": 56.38 + }, + { + "id": 350, + "title": "جاسك", + "slug": "Jask", + "province_id": 23, + "latitude": 25.643889, + "longitude": 57.774444 + }, + { + "id": 203, + "title": "جغتاي", + "slug": "Jaghatay", + "province_id": 10, + "latitude": 36.5788853, + "longitude": 57.251215 + }, + { + "id": 63, + "title": "جلفا", + "slug": "Jolfa", + "province_id": 4, + "latitude": 38.940278, + "longitude": 45.630833 + }, + { + "id": 317, + "title": "جم", + "slug": "Jam", + "province_id": 19, + "latitude": 27.827778, + "longitude": 52.326944 + }, + { + "id": 132, + "title": "جهرم", + "slug": "Jahrom", + "province_id": 8, + "latitude": 28.5, + "longitude": 53.560556 + }, + { + "id": 96, + "title": "جوانرود", + "slug": "Javanrood", + "province_id": 6, + "latitude": 36.418056, + "longitude": 54.976389 + }, + { + "id": 43, + "title": "جويبار", + "slug": "Juybar", + "province_id": 3, + "latitude": 36.8241289, + "longitude": 49.4237274 + }, + { + "id": 205, + "title": "جوين", + "slug": "Jovin", + "province_id": 10, + "latitude": 36.6362238, + "longitude": 57.5079912 + }, + { + "id": 160, + "title": "جيرفت", + "slug": "Jiroft", + "province_id": 9, + "latitude": 28.6751124, + "longitude": 57.7371569 + }, + { + "id": 352, + "title": "حاجي اباد", + "slug": "Hajiabadi", + "province_id": 23, + "latitude": 28.309167, + "longitude": 55.901667 + }, + { + "id": 343, + "title": "خاتم", + "slug": "Seal", + "province_id": 22, + "latitude": 28.1804287, + "longitude": 55.7453367 + }, + { + "id": 235, + "title": "خاش", + "slug": "Khash", + "province_id": 12, + "latitude": 28.216667, + "longitude": 61.2 + }, + { + "id": 320, + "title": "خدابنده", + "slug": "Khodabande", + "province_id": 20, + "latitude": 36.114722, + "longitude": 48.591111 + }, + { + "id": 282, + "title": "خرم آباد", + "slug": "Khorramabad", + "province_id": 16, + "latitude": 39.648333, + "longitude": 47.9175 + }, + { + "id": 146, + "title": "خرم بيد", + "slug": "Khorrambid", + "province_id": 8, + "latitude": 32.6708345, + "longitude": 51.6470279 + }, + { + "id": 323, + "title": "خرمدره", + "slug": "Khoramdeh", + "province_id": 20, + "latitude": 30.4256219, + "longitude": 48.1891185 + }, + { + "id": 108, + "title": "خرمشهر", + "slug": "Khorramshahr", + "province_id": 7, + "latitude": 37.758889, + "longitude": 45.978333 + }, + { + "id": 376, + "title": "خلخال", + "slug": "Anklet", + "province_id": 25, + "latitude": 37.618889, + "longitude": 48.525833 + }, + { + "id": 198, + "title": "خليل آباد", + "slug": "Khaliabad", + "province_id": 10, + "latitude": 35.255833, + "longitude": 58.286389 + }, + { + "id": 354, + "title": "خمير", + "slug": "Dough", + "province_id": 23, + "latitude": 26.952222, + "longitude": 55.585 + }, + { + "id": 4, + "title": "خمين", + "slug": "Khomain", + "province_id": 1, + "latitude": 33.6406148, + "longitude": 50.0771125 + }, + { + "id": 211, + "title": "خميني شهر", + "slug": "Khomeini Shahr", + "province_id": 11, + "latitude": 32.700278, + "longitude": 51.521111 + }, + { + "id": 152, + "title": "خنج", + "slug": "Khonj", + "province_id": 8, + "latitude": 27.891389, + "longitude": 53.434444 + }, + { + "id": 11, + "title": "خنداب", + "slug": "Varkaroud", + "province_id": 1, + "latitude": 37.340278, + "longitude": 46.056111 + }, + { + "id": 192, + "title": "خواف", + "slug": "KHAF", + "province_id": 10, + "latitude": 34.576389, + "longitude": 60.140833 + }, + { + "id": 212, + "title": "خوانسار", + "slug": "Khansar", + "province_id": 11, + "latitude": 33.220556, + "longitude": 50.315 + }, + { + "id": 422, + "title": "خوسف", + "slug": "Khoosf", + "province_id": 30, + "latitude": 34.576389, + "longitude": 60.140833 + }, + { + "id": 133, + "title": "داراب", + "slug": "Darab", + "province_id": 8, + "latitude": 37.940833, + "longitude": 47.536667 + }, + { + "id": 100, + "title": "دالاهو", + "slug": "Dalahoo", + "province_id": 6, + "latitude": 34.284167, + "longitude": 46.242222 + }, + { + "id": 327, + "title": "دامغان", + "slug": "Damghan", + "province_id": 21, + "latitude": 31.94, + "longitude": 51.647778 + }, + { + "id": 208, + "title": "داورزن", + "slug": "Davarzan", + "province_id": 10, + "latitude": 36.168333, + "longitude": 54.348056 + }, + { + "id": 414, + "title": "درميان", + "slug": "in the middle of", + "province_id": 30, + "latitude": 33.0339405, + "longitude": 60.1184797 + }, + { + "id": 292, + "title": "دره شهر", + "slug": "Valley City", + "province_id": 17, + "latitude": 33.139722, + "longitude": 47.376111 + }, + { + "id": 184, + "title": "درگز", + "slug": "Dargaz", + "province_id": 10, + "latitude": 37.444444, + "longitude": 59.108056 + }, + { + "id": 109, + "title": "دزفول", + "slug": "Dezful", + "province_id": 7, + "latitude": 32.3830777, + "longitude": 48.4235841 + }, + { + "id": 110, + "title": "دشت آزادگان", + "slug": "DashteAzadegan", + "province_id": 7, + "latitude": 30.347296, + "longitude": 48.2934004 + }, + { + "id": 311, + "title": "دشتستان", + "slug": "Dashtestan", + "province_id": 19, + "latitude": 29.266667, + "longitude": 51.216667 + }, + { + "id": 312, + "title": "دشتي", + "slug": "Dashti", + "province_id": 19, + "latitude": 35.7845145, + "longitude": 51.4347961 + }, + { + "id": 283, + "title": "دلفان", + "slug": "Delfan", + "province_id": 16, + "latitude": 38.940278, + "longitude": 45.630833 + }, + { + "id": 5, + "title": "دليجان", + "slug": "Delijan", + "province_id": 1, + "latitude": 32.4750168, + "longitude": 51.3050851 + }, + { + "id": 244, + "title": "دلگان", + "slug": "Dlgan", + "province_id": 12, + "latitude": 27.6077357, + "longitude": 59.4720904 + }, + { + "id": 359, + "title": "دماوند", + "slug": "Damavand", + "province_id": 24, + "latitude": 35.9467494, + "longitude": 52.1275481 + }, + { + "id": 304, + "title": "دنا", + "slug": "Dena", + "province_id": 18, + "latitude": 30.9516666, + "longitude": 51.4375 + }, + { + "id": 229, + "title": "دهاقان", + "slug": "Dehaghan", + "province_id": 11, + "latitude": 31.94, + "longitude": 51.647778 + }, + { + "id": 293, + "title": "دهلران", + "slug": "DEHLORAN", + "province_id": 17, + "latitude": 31.94, + "longitude": 51.647778 + }, + { + "id": 261, + "title": "دهگلان", + "slug": "Dehgolan", + "province_id": 13, + "latitude": 31.94, + "longitude": 51.647778 + }, + { + "id": 289, + "title": "دوره", + "slug": "the period", + "province_id": 16, + "latitude": 33.4955028, + "longitude": 49.0631743 + }, + { + "id": 284, + "title": "دورود", + "slug": "Dorud", + "province_id": 16, + "latitude": 34.806667, + "longitude": 46.488611 + }, + { + "id": 316, + "title": "ديلم", + "slug": "Daylam", + "province_id": 19, + "latitude": 30.1182632, + "longitude": 50.2261227 + }, + { + "id": 258, + "title": "ديواندره", + "slug": "Divandareh", + "province_id": 13, + "latitude": 35.913889, + "longitude": 47.023889 + }, + { + "id": 175, + "title": "رابر", + "slug": "Seal", + "province_id": 9, + "latitude": 37.940833, + "longitude": 47.536667 + }, + { + "id": 33, + "title": "رامسر", + "slug": "Ramsar", + "province_id": 3, + "latitude": 36.903056, + "longitude": 50.658333 + }, + { + "id": 120, + "title": "رامشير", + "slug": "Ramshir", + "province_id": 7, + "latitude": 30.4256219, + "longitude": 48.1891185 + }, + { + "id": 111, + "title": "رامهرمز", + "slug": "Rāmhormoz", + "province_id": 7, + "latitude": 31.28, + "longitude": 49.603611 + }, + { + "id": 401, + "title": "راميان", + "slug": "Ramian", + "province_id": 28, + "latitude": 33.2729171, + "longitude": 52.1985314 + }, + { + "id": 168, + "title": "راور", + "slug": "Raver", + "province_id": 9, + "latitude": 31.265556, + "longitude": 56.805556 + }, + { + "id": 365, + "title": "رباطكريم", + "slug": "Robat Karim", + "province_id": 24, + "latitude": 35.484722, + "longitude": 51.082778 + }, + { + "id": 269, + "title": "رزن", + "slug": "Razan", + "province_id": 14, + "latitude": 35.386667, + "longitude": 49.033889 + }, + { + "id": 154, + "title": "رستم", + "slug": "Rustam", + "province_id": 8, + "latitude": 37.2709152, + "longitude": 49.5969146 + }, + { + "id": 17, + "title": "رشت", + "slug": "Rasht", + "province_id": 2, + "latitude": 37.280833, + "longitude": 49.583056 + }, + { + "id": 196, + "title": "رشتخوار", + "slug": "Roshtkhar", + "province_id": 10, + "latitude": 34.974722, + "longitude": 59.623611 + }, + { + "id": 26, + "title": "رضوانشهر", + "slug": "Rezvanshahr", + "province_id": 2, + "latitude": 36.694444, + "longitude": 45.141667 + }, + { + "id": 161, + "title": "رفسنجان", + "slug": "Rafsanjan", + "province_id": 9, + "latitude": 32.4750168, + "longitude": 51.3050851 + }, + { + "id": 101, + "title": "روانسر", + "slug": "Rawansar", + "province_id": 6, + "latitude": 33.220556, + "longitude": 50.315 + }, + { + "id": 18, + "title": "رودبار", + "slug": "Rudbar", + "province_id": 2, + "latitude": 36.641111, + "longitude": 52.9125 + }, + { + "id": 172, + "title": "رودبارجنوب", + "slug": "Rvdbarjnvb", + "province_id": 9, + "latitude": 36.8241289, + "longitude": 49.4237274 + }, + { + "id": 19, + "title": "رودسر", + "slug": "Rudsar", + "province_id": 2, + "latitude": 37.1378415, + "longitude": 50.2836199 + }, + { + "id": 290, + "title": "رومشکان", + "slug": "Rvmshkan", + "province_id": 16, + "latitude": 36.521111, + "longitude": 46.208889 + }, + { + "id": 174, + "title": "ريگان", + "slug": "Reagan", + "province_id": 9, + "latitude": 27.6077357, + "longitude": 59.4720904 + }, + { + "id": 236, + "title": "زابل", + "slug": "Zabul", + "province_id": 12, + "latitude": 31.028611, + "longitude": 61.501111 + }, + { + "id": 237, + "title": "زاهدان", + "slug": "Zahedan", + "province_id": 12, + "latitude": 30.347296, + "longitude": 48.2934004 + }, + { + "id": 204, + "title": "زاوه", + "slug": "Zavh", + "province_id": 10, + "latitude": 35.2747322, + "longitude": 59.4677727 + }, + { + "id": 162, + "title": "زرند", + "slug": "Zarand", + "province_id": 9, + "latitude": 38.425117, + "longitude": 45.769636 + }, + { + "id": 9, + "title": "زرنديه", + "slug": "Zarandieh", + "province_id": 1, + "latitude": 30.812778, + "longitude": 56.563889 + }, + { + "id": 147, + "title": "زرين دشت", + "slug": "Zarrindasht", + "province_id": 8, + "latitude": 28.3545047, + "longitude": 54.4178006 + }, + { + "id": 321, + "title": "زنجان", + "slug": "Zanjan", + "province_id": 20, + "latitude": 32.4750168, + "longitude": 51.3050851 + }, + { + "id": 421, + "title": "زيرکوه", + "slug": "Zirkouh", + "province_id": 30, + "latitude": 31.1304036, + "longitude": 53.2503736 + }, + { + "id": 34, + "title": "سارئ", + "slug": "Infectious", + "province_id": 3, + "latitude": 36.563333, + "longitude": 53.06 + }, + { + "id": 278, + "title": "سامان", + "slug": "Order", + "province_id": 15, + "latitude": 31.94, + "longitude": 51.647778 + }, + { + "id": 425, + "title": "ساوجبلاغ", + "slug": "Savojbolagh", + "province_id": 31, + "latitude": 38.3787941, + "longitude": 47.4974359 + }, + { + "id": 6, + "title": "ساوه", + "slug": "Saveh", + "province_id": 1, + "latitude": 35.2747322, + "longitude": 59.4677727 + }, + { + "id": 185, + "title": "سبزوار", + "slug": "Sabzevar", + "province_id": 10, + "latitude": 36.2151823, + "longitude": 57.6678228 + }, + { + "id": 53, + "title": "سراب", + "slug": "Sarab", + "province_id": 4, + "latitude": 37.940833, + "longitude": 47.536667 + }, + { + "id": 238, + "title": "سراوان", + "slug": "Saravan", + "province_id": 12, + "latitude": 33.860278, + "longitude": 58.521667 + }, + { + "id": 418, + "title": "سرايان", + "slug": "Writers", + "province_id": 30, + "latitude": 33.860278, + "longitude": 58.521667 + }, + { + "id": 240, + "title": "سرباز", + "slug": "Soldier", + "province_id": 12, + "latitude": 37.940833, + "longitude": 47.536667 + }, + { + "id": 415, + "title": "سربيشه", + "slug": "Sarbishe", + "province_id": 30, + "latitude": 32.575556, + "longitude": 59.798333 + }, + { + "id": 193, + "title": "سرخس", + "slug": "Fern", + "province_id": 10, + "latitude": 37.940833, + "longitude": 47.536667 + }, + { + "id": 334, + "title": "سرخه", + "slug": "Sorkhe", + "province_id": 21, + "latitude": 36.545, + "longitude": 61.157778 + }, + { + "id": 74, + "title": "سردشت", + "slug": "Sardasht", + "province_id": 5, + "latitude": 36.155278, + "longitude": 45.478889 + }, + { + "id": 260, + "title": "سروآباد", + "slug": "Sarvabad", + "province_id": 13, + "latitude": 37.85, + "longitude": 46.833333 + }, + { + "id": 153, + "title": "سروستان", + "slug": "Sarvestan", + "province_id": 8, + "latitude": 33.376111, + "longitude": 52.369444 + }, + { + "id": 91, + "title": "سرپل ذهاب", + "slug": "Sarpolzohab", + "province_id": 6, + "latitude": 34.461111, + "longitude": 45.862778 + }, + { + "id": 254, + "title": "سقز", + "slug": "Turpentine", + "province_id": 13, + "latitude": 36.246389, + "longitude": 46.266389 + }, + { + "id": 288, + "title": "سلسله", + "slug": "Series", + "province_id": 16, + "latitude": 32.045776, + "longitude": 34.751639 + }, + { + "id": 75, + "title": "سلماس", + "slug": "Salmas", + "province_id": 5, + "latitude": 38.197222, + "longitude": 44.765278 + }, + { + "id": 328, + "title": "سمنان", + "slug": "Semnan", + "province_id": 21, + "latitude": 35.2255585, + "longitude": 54.4342138 + }, + { + "id": 213, + "title": "سميرم", + "slug": "Semirom", + "province_id": 11, + "latitude": 31.3988346, + "longitude": 51.567593 + }, + { + "id": 92, + "title": "سنقر", + "slug": "Falcon", + "province_id": 6, + "latitude": 34.783611, + "longitude": 47.600278 + }, + { + "id": 255, + "title": "سنندج", + "slug": "Sanandaj", + "province_id": 13, + "latitude": 35.3218748, + "longitude": 46.9861647 + }, + { + "id": 35, + "title": "سوادكوه", + "slug": "Savadkooh", + "province_id": 3, + "latitude": 36.3040255, + "longitude": 52.8852403 + }, + { + "id": 163, + "title": "سيرجان", + "slug": "Sirjan", + "province_id": 9, + "latitude": 33.860278, + "longitude": 58.521667 + }, + { + "id": 299, + "title": "سيروان", + "slug": "Sirvan", + "province_id": 17, + "latitude": 37.4092357, + "longitude": 57.9276184 + }, + { + "id": 134, + "title": "سپيدان", + "slug": "Sepidan", + "province_id": 8, + "latitude": 30.2425282, + "longitude": 51.9924185 + }, + { + "id": 112, + "title": "شادگان", + "slug": "Shadegan", + "province_id": 7, + "latitude": 32.768333, + "longitude": 50.628611 + }, + { + "id": 7, + "title": "شازند", + "slug": "Shazand", + "province_id": 1, + "latitude": 33.455833, + "longitude": 49.455556 + }, + { + "id": 329, + "title": "شاهرود", + "slug": "Anymore", + "province_id": 21, + "latitude": 37.477778, + "longitude": 47.050833 + }, + { + "id": 81, + "title": "شاهين دژ", + "slug": "Shahindezh", + "province_id": 5, + "latitude": 36.679167, + "longitude": 46.566944 + }, + { + "id": 224, + "title": "شاهين شهروميمه", + "slug": "Shahin Shhrvmymh", + "province_id": 11, + "latitude": 33.1271852, + "longitude": 51.5150077 + }, + { + "id": 60, + "title": "شبستر", + "slug": "Shabestar", + "province_id": 4, + "latitude": 38.180278, + "longitude": 45.702778 + }, + { + "id": 24, + "title": "شفت", + "slug": "Shafts", + "province_id": 2, + "latitude": 39.630631, + "longitude": -78.929542 + }, + { + "id": 361, + "title": "شميرانات", + "slug": "Shemiranat", + "province_id": 24, + "latitude": 35.9548021, + "longitude": 51.5991643 + }, + { + "id": 164, + "title": "شهربابك", + "slug": "Shahrbabak", + "province_id": 9, + "latitude": 30.116389, + "longitude": 55.118611 + }, + { + "id": 217, + "title": "شهرضا", + "slug": "Branch", + "province_id": 11, + "latitude": 36.2924452, + "longitude": 59.5677149 + }, + { + "id": 272, + "title": "شهركرد", + "slug": "Kord", + "province_id": 15, + "latitude": 32.325556, + "longitude": 50.864444 + }, + { + "id": 363, + "title": "شهريار", + "slug": "Sh.", + "province_id": 24, + "latitude": 35.659722, + "longitude": 51.059167 + }, + { + "id": 115, + "title": "شوش", + "slug": "Susa", + "province_id": 7, + "latitude": 32.194167, + "longitude": 48.243611 + }, + { + "id": 113, + "title": "شوشتر", + "slug": "Shushtar", + "province_id": 7, + "latitude": 32.194167, + "longitude": 48.243611 + }, + { + "id": 87, + "title": "شوط", + "slug": "Round", + "province_id": 5, + "latitude": 32.194167, + "longitude": 48.243611 + }, + { + "id": 135, + "title": "شيراز", + "slug": "Shiraz", + "province_id": 8, + "latitude": 29.5917677, + "longitude": 52.5836982 + }, + { + "id": 408, + "title": "شيروان", + "slug": "Shirvan", + "province_id": 29, + "latitude": 37.4092357, + "longitude": 57.9276184 + }, + { + "id": 97, + "title": "صحنه", + "slug": "Scene", + "province_id": 6, + "latitude": 35.9985999, + "longitude": 45.8823428 + }, + { + "id": 20, + "title": "صومعه سرا", + "slug": "Somesara", + "province_id": 2, + "latitude": 37.311667, + "longitude": 49.321944 + }, + { + "id": 324, + "title": "طارم", + "slug": "Tarom", + "province_id": 20, + "latitude": 36.95, + "longitude": 56.38 + }, + { + "id": 427, + "title": "طالقان", + "slug": "Taleghan", + "province_id": 31, + "latitude": 31.94, + "longitude": 51.647778 + }, + { + "id": 423, + "title": "طبس", + "slug": "Peaks", + "province_id": 30, + "latitude": 33.595833, + "longitude": 56.924444 + }, + { + "id": 16, + "title": "طوالش", + "slug": "Tvalsh", + "province_id": 2, + "latitude": 37, + "longitude": 48.4222222 + }, + { + "id": 46, + "title": "عباس آباد", + "slug": "Abbas Abad", + "province_id": 3, + "latitude": 37.85, + "longitude": 46.833333 + }, + { + "id": 69, + "title": "عجب شير", + "slug": "Ajabshir", + "province_id": 4, + "latitude": 37.4775, + "longitude": 45.894167 + }, + { + "id": 393, + "title": "علي آباد", + "slug": "Ali Abad", + "province_id": 28, + "latitude": 32.6324231, + "longitude": 51.3679914 + }, + { + "id": 169, + "title": "عنبرآباد", + "slug": "Anbarabad", + "province_id": 9, + "latitude": 35.952222, + "longitude": 50.6075 + }, + { + "id": 273, + "title": "فارسان", + "slug": "Farsan", + "province_id": 15, + "latitude": 33.376111, + "longitude": 52.369444 + }, + { + "id": 409, + "title": "فاروج", + "slug": "Faruj", + "province_id": 29, + "latitude": 37.231111, + "longitude": 58.218889 + }, + { + "id": 150, + "title": "فراشبند", + "slug": "Farashband", + "province_id": 8, + "latitude": 28.871389, + "longitude": 52.091667 + }, + { + "id": 12, + "title": "فراهان", + "slug": "FARAHAN", + "province_id": 1, + "latitude": 38.509722, + "longitude": 46.654444 + }, + { + "id": 419, + "title": "فردوس", + "slug": "Paradise", + "province_id": 30, + "latitude": 34.018611, + "longitude": 58.172222 + }, + { + "id": 429, + "title": "فرديس", + "slug": "Fardis", + "province_id": 31, + "latitude": 34.018611, + "longitude": 58.172222 + }, + { + "id": 214, + "title": "فريدن", + "slug": "Frieden", + "province_id": 11, + "latitude": 33.0214829, + "longitude": 50.3069088 + }, + { + "id": 215, + "title": "فريدونشهر", + "slug": "Fereidunshahr", + "province_id": 11, + "latitude": 32.941111, + "longitude": 50.121111 + }, + { + "id": 45, + "title": "فريدونكنار", + "slug": "Fereidoon'kenar", + "province_id": 3, + "latitude": 36.686389, + "longitude": 52.5225 + }, + { + "id": 194, + "title": "فريمان", + "slug": "FARIMAN", + "province_id": 10, + "latitude": 35.706944, + "longitude": 59.85 + }, + { + "id": 136, + "title": "فسا", + "slug": "Fasa", + "province_id": 8, + "latitude": 28.938333, + "longitude": 53.648333 + }, + { + "id": 216, + "title": "فلاورجان", + "slug": "Falavarjan", + "province_id": 11, + "latitude": 32.555278, + "longitude": 51.509722 + }, + { + "id": 251, + "title": "فنوج", + "slug": "Fenouj", + "province_id": 12, + "latitude": 27.9467603, + "longitude": 57.7062572 + }, + { + "id": 21, + "title": "فومن", + "slug": "Fooman", + "province_id": 2, + "latitude": 37.223889, + "longitude": 49.3125 + }, + { + "id": 137, + "title": "فيروزآباد", + "slug": "Firozabad", + "province_id": 8, + "latitude": 28.843889, + "longitude": 52.570833 + }, + { + "id": 367, + "title": "فيروزكوه", + "slug": "Firouzkouh", + "province_id": 24, + "latitude": 35.438671, + "longitude": 60.809387 + }, + { + "id": 36, + "title": "قائم شهر", + "slug": "Qaemshahr", + "province_id": 3, + "latitude": 35.5445805, + "longitude": 51.2302457 + }, + { + "id": 416, + "title": "قائنات", + "slug": "Ghaenat", + "province_id": 30, + "latitude": 33.726667, + "longitude": 59.184444 + }, + { + "id": 256, + "title": "قروه", + "slug": "Qorveh", + "province_id": 13, + "latitude": 35.1678934, + "longitude": 47.8038272 + }, + { + "id": 387, + "title": "قزوين", + "slug": "Qazvin", + "province_id": 27, + "latitude": 36.0881317, + "longitude": 49.8547266 + }, + { + "id": 348, + "title": "قشم", + "slug": "Qeshm", + "province_id": 23, + "latitude": 34.6399443, + "longitude": 50.8759419 + }, + { + "id": 93, + "title": "قصرشيرين", + "slug": "Qasr-e Shirin", + "province_id": 6, + "latitude": 34.5159031, + "longitude": 45.5776859 + }, + { + "id": 173, + "title": "قلعه گنج", + "slug": "Castle treasure", + "province_id": 9, + "latitude": 27.523611, + "longitude": 57.881111 + }, + { + "id": 384, + "title": "قم", + "slug": "Qom", + "province_id": 26, + "latitude": 34.6399443, + "longitude": 50.8759419 + }, + { + "id": 186, + "title": "قوچان", + "slug": "Ghoochan", + "province_id": 10, + "latitude": 37.106111, + "longitude": 58.509444 + }, + { + "id": 148, + "title": "قيروکارزين", + "slug": "Qyrvkarzyn", + "province_id": 8, + "latitude": 28.42998, + "longitude": 53.09516 + }, + { + "id": 187, + "title": "كاشمر", + "slug": "Kashmar", + "province_id": 10, + "latitude": 35.238333, + "longitude": 58.465556 + }, + { + "id": 259, + "title": "كامياران", + "slug": "Kamyaran", + "province_id": 13, + "latitude": 34.795556, + "longitude": 46.935556 + }, + { + "id": 266, + "title": "كبودرآهنگ", + "slug": "Kaboudarahang", + "province_id": 14, + "latitude": 35.208333, + "longitude": 48.723889 + }, + { + "id": 165, + "title": "كرمان", + "slug": "Kerman", + "province_id": 9, + "latitude": 35.706944, + "longitude": 59.85 + }, + { + "id": 197, + "title": "كلات", + "slug": "Kalat", + "province_id": 10, + "latitude": 34.198333, + "longitude": 58.544444 + }, + { + "id": 399, + "title": "كلاله", + "slug": "Stigma", + "province_id": 28, + "latitude": 37.380833, + "longitude": 55.491667 + }, + { + "id": 314, + "title": "كنگان", + "slug": "Kangan", + "province_id": 19, + "latitude": 27.8370437, + "longitude": 52.0645473 + }, + { + "id": 166, + "title": "كهنوج", + "slug": "Kahnooj", + "province_id": 9, + "latitude": 27.9467603, + "longitude": 57.7062572 + }, + { + "id": 380, + "title": "كوثر", + "slug": "Kosar", + "province_id": 25, + "latitude": 31.8676866, + "longitude": 54.3379802 + }, + { + "id": 171, + "title": "كوهبنان", + "slug": "Kuhbanan", + "province_id": 9, + "latitude": 31.541944, + "longitude": 60.036389 + }, + { + "id": 276, + "title": "كوهرنگ", + "slug": "Kouhrang", + "province_id": 15, + "latitude": 32.5558364, + "longitude": 51.6787252 + }, + { + "id": 139, + "title": "لارستان", + "slug": "LARESTAN", + "province_id": 8, + "latitude": 33.376111, + "longitude": 52.369444 + }, + { + "id": 118, + "title": "لالي", + "slug": "Lali", + "province_id": 7, + "latitude": 32.328889, + "longitude": 49.093611 + }, + { + "id": 143, + "title": "لامرد", + "slug": "Lamerd", + "province_id": 8, + "latitude": 27.3423771, + "longitude": 53.1803558 + }, + { + "id": 23, + "title": "لاهيجان", + "slug": "LAHIJAN", + "province_id": 2, + "latitude": 32.555278, + "longitude": 51.509722 + }, + { + "id": 274, + "title": "لردگان", + "slug": "LORDEGAN", + "province_id": 15, + "latitude": 32.768333, + "longitude": 50.628611 + }, + { + "id": 220, + "title": "لنجان", + "slug": "Lenjan", + "province_id": 11, + "latitude": 32.4750168, + "longitude": 51.3050851 + }, + { + "id": 308, + "title": "لنده", + "slug": "Landhi", + "province_id": 18, + "latitude": 36.955278, + "longitude": 45.388056 + }, + { + "id": 22, + "title": "لنگرود", + "slug": "Langerud", + "province_id": 2, + "latitude": 37.196944, + "longitude": 50.153611 + }, + { + "id": 28, + "title": "ماسال", + "slug": "Masal", + "province_id": 2, + "latitude": 37.3621185, + "longitude": 49.1314769 + }, + { + "id": 76, + "title": "ماكو", + "slug": "Maku", + "province_id": 5, + "latitude": 39.295278, + "longitude": 44.516667 + }, + { + "id": 410, + "title": "مانه وسملقان", + "slug": "Manet Vsmlqan", + "province_id": 29, + "latitude": 37.6620614, + "longitude": 56.741207 + }, + { + "id": 325, + "title": "ماهنشان", + "slug": "Mahneshan", + "province_id": 20, + "latitude": 33.122222, + "longitude": 46.164722 + }, + { + "id": 225, + "title": "مبارکه", + "slug": "Mobarakeh", + "province_id": 11, + "latitude": 32.346389, + "longitude": 51.504444 + }, + { + "id": 8, + "title": "محلات", + "slug": "Mahalat", + "province_id": 1, + "latitude": 33.9085748, + "longitude": 50.4552616 + }, + { + "id": 40, + "title": "محمودآباد", + "slug": "Mahmudabad", + "province_id": 3, + "latitude": 28.843889, + "longitude": 52.570833 + }, + { + "id": 54, + "title": "مراغه", + "slug": "Maragheh", + "province_id": 4, + "latitude": 37.389167, + "longitude": 46.2375 + }, + { + "id": 55, + "title": "مرند", + "slug": "Marand", + "province_id": 4, + "latitude": 38.425117, + "longitude": 45.769636 + }, + { + "id": 140, + "title": "مرودشت", + "slug": "MARVDASHT", + "province_id": 8, + "latitude": 29.874167, + "longitude": 52.8025 + }, + { + "id": 257, + "title": "مريوان", + "slug": "Marivan", + "province_id": 13, + "latitude": 35.526944, + "longitude": 46.176389 + }, + { + "id": 114, + "title": "مسجدسليمان", + "slug": "Masjed Soleiman", + "province_id": 7, + "latitude": 31.936389, + "longitude": 49.303889 + }, + { + "id": 189, + "title": "مشهد", + "slug": "Mashhad", + "province_id": 10, + "latitude": 38.425117, + "longitude": 45.769636 + }, + { + "id": 377, + "title": "مشگين شهر", + "slug": "Meshkinshar", + "province_id": 25, + "latitude": 32.941111, + "longitude": 50.121111 + }, + { + "id": 369, + "title": "ملارد", + "slug": "Mallard", + "province_id": 24, + "latitude": 27.3423771, + "longitude": 53.1803558 + }, + { + "id": 263, + "title": "ملاير", + "slug": "Malayer", + "province_id": 14, + "latitude": 34.296944, + "longitude": 48.823611 + }, + { + "id": 64, + "title": "ملکان", + "slug": "Malakan", + "province_id": 4, + "latitude": 37.145625, + "longitude": 46.1685242 + }, + { + "id": 141, + "title": "ممسني", + "slug": "Mamasani", + "province_id": 8, + "latitude": 31.9600345, + "longitude": 50.5122652 + }, + { + "id": 170, + "title": "منوجان", + "slug": "Manoujan", + "province_id": 9, + "latitude": 32.4750168, + "longitude": 51.3050851 + }, + { + "id": 199, + "title": "مه ولات", + "slug": "Mahvelat", + "province_id": 10, + "latitude": 35.0210829, + "longitude": 58.7818116 + }, + { + "id": 77, + "title": "مهاباد", + "slug": "Mahabad", + "province_id": 5, + "latitude": 36.763056, + "longitude": 45.722222 + }, + { + "id": 331, + "title": "مهدئ شهر", + "slug": "Mehdi City", + "province_id": 21, + "latitude": 35.7, + "longitude": 53.35 + }, + { + "id": 149, + "title": "مهر", + "slug": "stamp", + "province_id": 8, + "latitude": 33.122222, + "longitude": 46.164722 + }, + { + "id": 295, + "title": "مهران", + "slug": "Mehran", + "province_id": 17, + "latitude": 33.122222, + "longitude": 46.164722 + }, + { + "id": 245, + "title": "مهرستان", + "slug": "Your spouse", + "province_id": 12, + "latitude": 33.122222, + "longitude": 46.164722 + }, + { + "id": 338, + "title": "مهريز", + "slug": "MEHRIZ", + "province_id": 22, + "latitude": 31.28, + "longitude": 49.603611 + }, + { + "id": 78, + "title": "مياندوآب", + "slug": "Miandoab", + "province_id": 5, + "latitude": 36.969444, + "longitude": 46.102778 + }, + { + "id": 56, + "title": "ميانه", + "slug": "Mianeh", + "province_id": 4, + "latitude": 37.421111, + "longitude": 47.715 + }, + { + "id": 340, + "title": "ميبد", + "slug": "Meibod", + "province_id": 22, + "latitude": 32.2487226, + "longitude": 54.0079341 + }, + { + "id": 349, + "title": "ميناب", + "slug": "Minab", + "province_id": 23, + "latitude": 27.146667, + "longitude": 57.08 + }, + { + "id": 397, + "title": "مينودشت", + "slug": "Minoodasht", + "province_id": 28, + "latitude": 29.874167, + "longitude": 52.8025 + }, + { + "id": 222, + "title": "نجف آباد", + "slug": "Najaf Abad", + "province_id": 11, + "latitude": 32.6324231, + "longitude": 51.3679914 + }, + { + "id": 178, + "title": "نرماشير", + "slug": "Narmashir", + "province_id": 9, + "latitude": 30.558889, + "longitude": 49.198056 + }, + { + "id": 223, + "title": "نطنز", + "slug": "Natanz", + "province_id": 11, + "latitude": 33.513333, + "longitude": 51.916389 + }, + { + "id": 426, + "title": "نظرآباد", + "slug": "Nazarabad", + "province_id": 31, + "latitude": 39.648333, + "longitude": 47.9175 + }, + { + "id": 79, + "title": "نقده", + "slug": "Tinsel", + "province_id": 5, + "latitude": 36.955278, + "longitude": 45.388056 + }, + { + "id": 381, + "title": "نمين", + "slug": "Namin", + "province_id": 25, + "latitude": 38.426944, + "longitude": 48.483889 + }, + { + "id": 264, + "title": "نهاوند", + "slug": "Skinheads", + "province_id": 14, + "latitude": 35.9467494, + "longitude": 52.1275481 + }, + { + "id": 417, + "title": "نهبندان", + "slug": "Nehbandan", + "province_id": 30, + "latitude": 31.541944, + "longitude": 60.036389 + }, + { + "id": 37, + "title": "نور", + "slug": "Nur", + "province_id": 3, + "latitude": 36.648889, + "longitude": 51.496111 + }, + { + "id": 38, + "title": "نوشهر", + "slug": "Noshahr", + "province_id": 3, + "latitude": 36.648889, + "longitude": 51.496111 + }, + { + "id": 382, + "title": "نير", + "slug": "Nir", + "province_id": 25, + "latitude": 50.3851246, + "longitude": 3.2642436 + }, + { + "id": 190, + "title": "نيشابور", + "slug": "Neyshabur", + "province_id": 10, + "latitude": 36.2140865, + "longitude": 58.7960915 + }, + { + "id": 239, + "title": "نيك شهر", + "slug": "Nikshahr", + "province_id": 12, + "latitude": 36.648889, + "longitude": 51.496111 + }, + { + "id": 98, + "title": "هرسين", + "slug": "Harsin", + "province_id": 6, + "latitude": 34.2719149, + "longitude": 47.6046183 + }, + { + "id": 62, + "title": "هريس", + "slug": "Harris", + "province_id": 4, + "latitude": 29.7751825, + "longitude": -95.3102505 + }, + { + "id": 57, + "title": "هشترود", + "slug": "Hashtrood", + "province_id": 4, + "latitude": 37.477778, + "longitude": 47.050833 + }, + { + "id": 265, + "title": "همدان", + "slug": "Hamedan", + "province_id": 14, + "latitude": 31.541944, + "longitude": 60.036389 + }, + { + "id": 119, + "title": "هنديجان", + "slug": "Hendijan", + "province_id": 7, + "latitude": 32.4750168, + "longitude": 51.3050851 + }, + { + "id": 362, + "title": "ورامين", + "slug": "Varamin", + "province_id": 24, + "latitude": 35.3252407, + "longitude": 51.6471987 + }, + { + "id": 68, + "title": "ورزقان", + "slug": "Varzeghan", + "province_id": 4, + "latitude": 38.509722, + "longitude": 46.654444 + }, + { + "id": 379, + "title": "پارس آباد", + "slug": "Pars Abad", + "province_id": 25, + "latitude": 39.648333, + "longitude": 47.9175 + }, + { + "id": 355, + "title": "پارسيان", + "slug": "Parsian", + "province_id": 23, + "latitude": 32.2582066, + "longitude": 50.5705088 + }, + { + "id": 151, + "title": "پاسارگاد", + "slug": "Pasargad", + "province_id": 8, + "latitude": 39.648333, + "longitude": 47.9175 + }, + { + "id": 366, + "title": "پاكدشت", + "slug": "Pakdasht", + "province_id": 24, + "latitude": 35.4668913, + "longitude": 51.6860625 + }, + { + "id": 90, + "title": "پاوه", + "slug": "Pave", + "province_id": 6, + "latitude": 29.579167, + "longitude": 50.516944 + }, + { + "id": 372, + "title": "پرديس", + "slug": "College", + "province_id": 24, + "latitude": 34.018611, + "longitude": 58.172222 + }, + { + "id": 287, + "title": "پلدختر", + "slug": "Poldokhtar", + "province_id": 16, + "latitude": 33.153611, + "longitude": 47.713611 + }, + { + "id": 85, + "title": "پلدشت", + "slug": "Demands", + "province_id": 5, + "latitude": 33.153611, + "longitude": 47.713611 + }, + { + "id": 72, + "title": "پيرانشهر", + "slug": "Piranshar", + "province_id": 5, + "latitude": 36.694444, + "longitude": 45.141667 + }, + { + "id": 228, + "title": "چادگان", + "slug": "Chadegan", + "province_id": 11, + "latitude": 39.0649837, + "longitude": 44.3844679 + }, + { + "id": 67, + "title": "چاراويماق", + "slug": "Charoymagh", + "province_id": 4, + "latitude": 37.1299052, + "longitude": 47.0245686 + }, + { + "id": 84, + "title": "چالدران", + "slug": "Chalderan", + "province_id": 5, + "latitude": 39.0649837, + "longitude": 44.3844679 + }, + { + "id": 42, + "title": "چالوس", + "slug": "Chalus", + "province_id": 3, + "latitude": 36.6459174, + "longitude": 51.406979 + }, + { + "id": 234, + "title": "چابهار", + "slug": "ChaBahar", + "province_id": 12, + "latitude": 25.291944, + "longitude": 60.643056 + }, + { + "id": 191, + "title": "چناران", + "slug": "CHENARAN", + "province_id": 10, + "latitude": 39.0649837, + "longitude": 44.3844679 + }, + { + "id": 128, + "title": "کارون", + "slug": "Karun", + "province_id": 7, + "latitude": 29.619444, + "longitude": 51.654167 + }, + { + "id": 138, + "title": "کازرون", + "slug": "Kazeroon", + "province_id": 8, + "latitude": 29.619444, + "longitude": 51.654167 + }, + { + "id": 218, + "title": "کاشان", + "slug": "Kashan", + "province_id": 11, + "latitude": 33.9850358, + "longitude": 51.4099625 + }, + { + "id": 424, + "title": "کرج", + "slug": "Karaj", + "province_id": 31, + "latitude": 35.8400188, + "longitude": 50.9390906 + }, + { + "id": 89, + "title": "کرمانشاه", + "slug": "Kermanshah", + "province_id": 6, + "latitude": 29.4850089, + "longitude": 57.6439048 + }, + { + "id": 50, + "title": "کلاردشت", + "slug": "Kelardasht", + "province_id": 3, + "latitude": 36.155278, + "longitude": 45.478889 + }, + { + "id": 61, + "title": "کليبر", + "slug": "Kalibar", + "province_id": 4, + "latitude": 38.869444, + "longitude": 47.035556 + }, + { + "id": 10, + "title": "کميجان", + "slug": "Komijan", + "province_id": 1, + "latitude": 27.4475626, + "longitude": 57.5051616 + }, + { + "id": 94, + "title": "کنگاور", + "slug": "Kangavar", + "province_id": 6, + "latitude": 27.8370437, + "longitude": 52.0645473 + }, + { + "id": 156, + "title": "کوار", + "slug": "Kovar", + "province_id": 8, + "latitude": 31.8676866, + "longitude": 54.3379802 + }, + { + "id": 285, + "title": "کوهدشت", + "slug": "Kuhdasht", + "province_id": 16, + "latitude": 33.535, + "longitude": 47.606111 + }, + { + "id": 121, + "title": "گتوند", + "slug": "Scroll down", + "province_id": 7, + "latitude": 32.251389, + "longitude": 48.816111 + }, + { + "id": 330, + "title": "گرمسار", + "slug": "Garmsar", + "province_id": 21, + "latitude": 35.218333, + "longitude": 52.340833 + }, + { + "id": 411, + "title": "گرمه", + "slug": "it is hot", + "province_id": 29, + "latitude": 39.0372267, + "longitude": 47.9277021 + }, + { + "id": 378, + "title": "گرمي", + "slug": "G", + "province_id": 25, + "latitude": 39.0372267, + "longitude": 47.9277021 + }, + { + "id": 395, + "title": "گرگان", + "slug": "Gorgan", + "province_id": 28, + "latitude": 36.8456427, + "longitude": 54.4393363 + }, + { + "id": 44, + "title": "گلوگاه", + "slug": "Galugah", + "province_id": 3, + "latitude": 36.727222, + "longitude": 53.808889 + }, + { + "id": 219, + "title": "گلپايگان", + "slug": "Golpaygan", + "province_id": 11, + "latitude": 33.453611, + "longitude": 50.288333 + }, + { + "id": 188, + "title": "گناباد", + "slug": "GONABAD", + "province_id": 10, + "latitude": 36.763056, + "longitude": 45.722222 + }, + { + "id": 315, + "title": "گناوه", + "slug": "Ganaveh", + "province_id": 19, + "latitude": 29.579167, + "longitude": 50.516944 + }, + { + "id": 396, + "title": "گنبدكاووس", + "slug": "Gonbad", + "province_id": 28, + "latitude": 37.25, + "longitude": 55.167222 + }, + { + "id": 95, + "title": "گيلانغرب", + "slug": "Gilangharb", + "province_id": 6, + "latitude": 34.142222, + "longitude": 45.920278 + }, + { + "id": 303, + "title": "گچساران", + "slug": "Gachsaran", + "province_id": 18, + "latitude": 36.645556, + "longitude": 59.121111 + } +] diff --git a/src/database/seeds/data/iran-provinces.json b/src/database/seeds/data/iran-provinces.json new file mode 100644 index 0000000..87aa3bc --- /dev/null +++ b/src/database/seeds/data/iran-provinces.json @@ -0,0 +1,219 @@ +[ + { + "id": 4, + "title": "آذربایجان شرقی", + "slug": "East Azarbaijan", + "latitude": 37.9035733, + "longitude": 46.2682109 + }, + { + "id": 5, + "title": "آذربایجان غربی", + "slug": "Western Azerbaijan", + "latitude": 37.7208556, + "longitude": 45.0126837 + }, + { + "id": 25, + "title": "اردبیل", + "slug": "Ardabil", + "latitude": 38.4853276, + "longitude": 47.8911209 + }, + { + "id": 11, + "title": "اصفهان", + "slug": "Esfahan", + "latitude": 32.6546275, + "longitude": 51.6679826 + }, + { + "id": 31, + "title": "البرز", + "slug": "Alborz", + "latitude": 35.9960467, + "longitude": 50.9289246 + }, + { + "id": 17, + "title": "ایلام", + "slug": "Ilam", + "latitude": 33.2957618, + "longitude": 46.670534 + }, + { + "id": 19, + "title": "بوشهر", + "slug": "Bushehr", + "latitude": 28.9233837, + "longitude": 50.820314 + }, + { + "id": 24, + "title": "تهران", + "slug": "Tehran", + "latitude": 35.696111, + "longitude": 51.423056 + }, + { + "id": 30, + "title": "خراسان جنوبی", + "slug": "South Khorasan", + "latitude": 32.5175643, + "longitude": 59.1041758 + }, + { + "id": 10, + "title": "خراسان رضوی", + "slug": "Khorasan Razavi", + "latitude": 35.5160063, + "longitude": 59.2230448 + }, + { + "id": 29, + "title": "خراسان شمالی", + "slug": "North Khorasan", + "latitude": 37.4710353, + "longitude": 57.1013188 + }, + { + "id": 7, + "title": "خوزستان", + "slug": "Khuzestan", + "latitude": 31.4360149, + "longitude": 49.041312 + }, + { + "id": 20, + "title": "زنجان", + "slug": "Zanjan", + "latitude": 36.5018185, + "longitude": 48.3988186 + }, + { + "id": 21, + "title": "سمنان", + "slug": "Semnan", + "latitude": 35.2255585, + "longitude": 54.4342138 + }, + { + "id": 12, + "title": "سیستان وبلوچستان", + "slug": "Sistan and Baluchistan", + "latitude": 27.5299906, + "longitude": 60.5820676 + }, + { + "id": 8, + "title": "فارس", + "slug": "Fars", + "latitude": 29.1043813, + "longitude": 53.045893 + }, + { + "id": 27, + "title": "قزوین", + "slug": "Qazvin", + "latitude": 36.0881317, + "longitude": 49.8547266 + }, + { + "id": 26, + "title": "قم", + "slug": "Qom", + "latitude": 34.6399443, + "longitude": 50.8759419 + }, + { + "id": 13, + "title": "کردستان", + "slug": "Kurdistan", + "latitude": 35.6680062, + "longitude": 47.0027204 + }, + { + "id": 9, + "title": "کرمان", + "slug": "Kerman", + "latitude": 30.2839379, + "longitude": 57.0833628 + }, + { + "id": 6, + "title": "کرمانشاه", + "slug": "Kermanshah", + "latitude": 34.314167, + "longitude": 47.065 + }, + { + "id": 18, + "title": "کهگیلویه و بویراحمد", + "slug": "Kohgiloyeh Boyerahmad", + "latitude": 30.6509479, + "longitude": 51.60525 + }, + { + "id": 28, + "title": "گلستان", + "slug": "Golestan", + "latitude": 37.2898123, + "longitude": 55.1375834 + }, + { + "id": 2, + "title": "گیلان", + "slug": "Gilan", + "latitude": 37.2809, + "longitude": 49.5924 + }, + { + "id": 16, + "title": "لرستان", + "slug": "Lorestan", + "latitude": 33.4529829, + "longitude": 48.306995 + }, + { + "id": 3, + "title": "مازندران", + "slug": "Mazandaran", + "latitude": 36.2262393, + "longitude": 52.5318604 + }, + { + "id": 1, + "title": "مرکزی", + "slug": "Markazi", + "latitude": 34.6123, + "longitude": 49.8547 + }, + { + "id": 23, + "title": "هرمزگان", + "slug": "Hormozgan", + "latitude": 27.138723, + "longitude": 55.1375834 + }, + { + "id": 14, + "title": "همدان", + "slug": "Hamedan", + "latitude": 34.9277212, + "longitude": 48.6655971 + }, + { + "id": 15, + "title": "چهارمحال و بختیاری", + "slug": "Chaharmahal and Bakhtiari ", + "latitude": 31.9614348, + "longitude": 50.8456323 + }, + { + "id": 22, + "title": "یزد", + "slug": "Yazd", + "latitude": 32.0025525, + "longitude": 54.687334 + } +] diff --git a/src/provinces/dto/create-city.dto.ts b/src/provinces/dto/create-city.dto.ts new file mode 100644 index 0000000..6d8f242 --- /dev/null +++ b/src/provinces/dto/create-city.dto.ts @@ -0,0 +1,13 @@ +import { IsNotEmpty, IsString, Length } from 'class-validator'; + +export class CreateCityDto { + @IsString() + @Length(1, 100) + @IsNotEmpty() + name: string; + + @IsString() + @Length(1, 100) + @IsNotEmpty() + slug: string; +} diff --git a/src/provinces/dto/create-province.dto.ts b/src/provinces/dto/create-province.dto.ts new file mode 100644 index 0000000..8ce7da6 --- /dev/null +++ b/src/provinces/dto/create-province.dto.ts @@ -0,0 +1,13 @@ +import { IsNotEmpty, IsString, Length } from 'class-validator'; + +export class CreateProvinceDto { + @IsString() + @Length(1, 100) + @IsNotEmpty() + name: string; + + @IsString() + @Length(1, 100) + @IsNotEmpty() + slug: string; +} diff --git a/src/provinces/dto/update-city.dto.ts b/src/provinces/dto/update-city.dto.ts new file mode 100644 index 0000000..688ff39 --- /dev/null +++ b/src/provinces/dto/update-city.dto.ts @@ -0,0 +1,13 @@ +import { IsOptional, IsString, Length } from 'class-validator'; + +export class UpdateCityDto { + @IsOptional() + @IsString() + @Length(1, 100) + name?: string; + + @IsOptional() + @IsString() + @Length(1, 100) + slug?: string; +} diff --git a/src/provinces/dto/update-province.dto.ts b/src/provinces/dto/update-province.dto.ts new file mode 100644 index 0000000..8ba392a --- /dev/null +++ b/src/provinces/dto/update-province.dto.ts @@ -0,0 +1,13 @@ +import { IsOptional, IsString, Length } from 'class-validator'; + +export class UpdateProvinceDto { + @IsOptional() + @IsString() + @Length(1, 100) + name?: string; + + @IsOptional() + @IsString() + @Length(1, 100) + slug?: string; +} diff --git a/src/provinces/entities/city.entity.ts b/src/provinces/entities/city.entity.ts new file mode 100644 index 0000000..9c896b9 --- /dev/null +++ b/src/provinces/entities/city.entity.ts @@ -0,0 +1,37 @@ +import { + Column, + CreateDateColumn, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { Province } from './province.entity'; + +@Entity('cities') +export class City { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + provinceId: string; + + @ManyToOne(() => Province, (province) => province.cities, { + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'provinceId' }) + province: Province; + + @Column({ length: 100 }) + name: string; + + @Column({ length: 100, unique: true }) + slug: string; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/provinces/entities/province.entity.ts b/src/provinces/entities/province.entity.ts new file mode 100644 index 0000000..1cd138d --- /dev/null +++ b/src/provinces/entities/province.entity.ts @@ -0,0 +1,30 @@ +import { + Column, + CreateDateColumn, + Entity, + OneToMany, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { City } from './city.entity'; + +@Entity('provinces') +export class Province { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ length: 100, unique: true }) + name: string; + + @Column({ length: 100, unique: true }) + slug: string; + + @OneToMany(() => City, (city) => city.province) + cities: City[]; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/provinces/provinces.controller.ts b/src/provinces/provinces.controller.ts new file mode 100644 index 0000000..7051b92 --- /dev/null +++ b/src/provinces/provinces.controller.ts @@ -0,0 +1,155 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseUUIDPipe, + Patch, + Post, + UseGuards, +} from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { CurrentUser } from '../auth/decorators/current-user.decorator'; +import { Public } from '../auth/decorators/public.decorator'; +import { Permissions } from '../auth/decorators/permissions.decorator'; +import { Roles } from '../auth/decorators/roles.decorator'; +import { PermissionsGuard } from '../auth/guards/permissions.guard'; +import { RolesGuard } from '../auth/guards/roles.guard'; +import { AuthUser } from '../auth/interfaces/auth-user.interface'; +import { PermissionCode } from '../authorization/enums/permission-code.enum'; +import { UserRole } from '../users/enums/user-role.enum'; +import { CreateCityDto } from './dto/create-city.dto'; +import { CreateProvinceDto } from './dto/create-province.dto'; +import { UpdateCityDto } from './dto/update-city.dto'; +import { UpdateProvinceDto } from './dto/update-province.dto'; +import { City } from './entities/city.entity'; +import { Province } from './entities/province.entity'; +import { ProvincesService } from './provinces.service'; +import { ApiPublicEndpoint } from '../swagger/decorators/swagger-auth.decorator'; +import { SWAGGER_JWT_AUTH } from '../swagger/swagger.setup'; +import { + ApiStandardController, + ApiStandardEmptyResponse, + ApiStandardOkResponse, +} from '../swagger/decorators/swagger-response.decorator'; + +@ApiTags('Provinces') +@ApiBearerAuth(SWAGGER_JWT_AUTH) +@ApiStandardController() +@Controller('provinces') +@UseGuards(RolesGuard, PermissionsGuard) +export class ProvincesController { + constructor(private readonly provincesService: ProvincesService) {} + + @Roles(UserRole.ADMIN) + @Permissions(PermissionCode.PROVINCES_WRITE) + @ApiStandardOkResponse(Province) + @Post() + create( + @Body() createProvinceDto: CreateProvinceDto, + @CurrentUser() user: AuthUser, + ) { + return this.provincesService.create(createProvinceDto, user); + } + + @Public() + @ApiPublicEndpoint('List all provinces') + @ApiStandardOkResponse(Province, { isArray: true }) + @Get() + findAll() { + return this.provincesService.findAll(); + } + + @Public() + @ApiPublicEndpoint('Get province by ID') + @ApiStandardOkResponse(Province) + @Get(':id') + findOne(@Param('id', ParseUUIDPipe) id: string) { + return this.provincesService.findOne(id); + } + + @Roles(UserRole.ADMIN) + @Permissions(PermissionCode.PROVINCES_WRITE) + @ApiStandardOkResponse(Province) + @Patch(':id') + update( + @Param('id', ParseUUIDPipe) id: string, + @Body() updateProvinceDto: UpdateProvinceDto, + @CurrentUser() user: AuthUser, + ) { + return this.provincesService.update(id, updateProvinceDto, user); + } + + @Roles(UserRole.ADMIN) + @Permissions(PermissionCode.PROVINCES_DELETE) + @ApiStandardEmptyResponse() + @Delete(':id') + remove( + @Param('id', ParseUUIDPipe) id: string, + @CurrentUser() user: AuthUser, + ) { + return this.provincesService.remove(id, user); + } + + @Roles(UserRole.ADMIN) + @Permissions(PermissionCode.PROVINCES_WRITE) + @ApiStandardOkResponse(City) + @Post(':provinceId/cities') + createCity( + @Param('provinceId', ParseUUIDPipe) provinceId: string, + @Body() createCityDto: CreateCityDto, + @CurrentUser() user: AuthUser, + ) { + return this.provincesService.createCity(provinceId, createCityDto, user); + } + + @Public() + @ApiPublicEndpoint('List cities for a province') + @ApiStandardOkResponse(City, { isArray: true }) + @Get(':provinceId/cities') + findCities(@Param('provinceId', ParseUUIDPipe) provinceId: string) { + return this.provincesService.findCities(provinceId); + } + + @Public() + @ApiPublicEndpoint('Get a city by ID') + @ApiStandardOkResponse(City) + @Get(':provinceId/cities/:cityId') + findCity( + @Param('provinceId', ParseUUIDPipe) provinceId: string, + @Param('cityId', ParseUUIDPipe) cityId: string, + ) { + return this.provincesService.findCity(provinceId, cityId); + } + + @Roles(UserRole.ADMIN) + @Permissions(PermissionCode.PROVINCES_WRITE) + @ApiStandardOkResponse(City) + @Patch(':provinceId/cities/:cityId') + updateCity( + @Param('provinceId', ParseUUIDPipe) provinceId: string, + @Param('cityId', ParseUUIDPipe) cityId: string, + @Body() updateCityDto: UpdateCityDto, + @CurrentUser() user: AuthUser, + ) { + return this.provincesService.updateCity( + provinceId, + cityId, + updateCityDto, + user, + ); + } + + @Roles(UserRole.ADMIN) + @Permissions(PermissionCode.PROVINCES_DELETE) + @ApiStandardEmptyResponse() + @Delete(':provinceId/cities/:cityId') + removeCity( + @Param('provinceId', ParseUUIDPipe) provinceId: string, + @Param('cityId', ParseUUIDPipe) cityId: string, + @CurrentUser() user: AuthUser, + ) { + return this.provincesService.removeCity(provinceId, cityId, user); + } +} diff --git a/src/provinces/provinces.module.ts b/src/provinces/provinces.module.ts new file mode 100644 index 0000000..a5d0fab --- /dev/null +++ b/src/provinces/provinces.module.ts @@ -0,0 +1,20 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { AuthGuardsModule } from '../auth/auth-guards.module'; +import { AuthorizationModule } from '../authorization/authorization.module'; +import { City } from './entities/city.entity'; +import { Province } from './entities/province.entity'; +import { ProvincesController } from './provinces.controller'; +import { ProvincesService } from './provinces.service'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Province, City]), + AuthorizationModule, + AuthGuardsModule, + ], + controllers: [ProvincesController], + providers: [ProvincesService], + exports: [ProvincesService], +}) +export class ProvincesModule {} diff --git a/src/provinces/provinces.service.ts b/src/provinces/provinces.service.ts new file mode 100644 index 0000000..439335f --- /dev/null +++ b/src/provinces/provinces.service.ts @@ -0,0 +1,146 @@ +import { + ForbiddenException, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { ProvincePolicy } from '../authorization/policies/province.policy'; +import { AuthUser } from '../auth/interfaces/auth-user.interface'; +import { CreateCityDto } from './dto/create-city.dto'; +import { CreateProvinceDto } from './dto/create-province.dto'; +import { UpdateCityDto } from './dto/update-city.dto'; +import { UpdateProvinceDto } from './dto/update-province.dto'; +import { City } from './entities/city.entity'; +import { Province } from './entities/province.entity'; + +@Injectable() +export class ProvincesService { + constructor( + @InjectRepository(Province) + private readonly provincesRepository: Repository, + @InjectRepository(City) + private readonly citiesRepository: Repository, + private readonly provincePolicy: ProvincePolicy, + ) {} + + create( + createProvinceDto: CreateProvinceDto, + user: AuthUser, + ): Promise { + this.ensureCanManage(user); + const province = this.provincesRepository.create(createProvinceDto); + return this.provincesRepository + .save(province) + .then((saved) => this.findOne(saved.id)); + } + + findAll(): Promise { + return this.provincesRepository.find({ + relations: { cities: true }, + order: { name: 'ASC', cities: { name: 'ASC' } }, + }); + } + + async findOne(id: string): Promise { + const province = await this.provincesRepository.findOne({ + where: { id }, + relations: { cities: true }, + order: { cities: { name: 'ASC' } }, + }); + if (!province) { + throw new NotFoundException(`Province #${id} not found`); + } + return province; + } + + async update( + id: string, + updateProvinceDto: UpdateProvinceDto, + user: AuthUser, + ): Promise { + this.ensureCanManage(user); + const province = await this.findOne(id); + Object.assign(province, updateProvinceDto); + await this.provincesRepository.save(province); + return this.findOne(id); + } + + async remove(id: string, user: AuthUser): Promise { + this.ensureCanManage(user); + const province = await this.findOne(id); + await this.provincesRepository.remove(province); + } + + async createCity( + provinceId: string, + createCityDto: CreateCityDto, + user: AuthUser, + ): Promise { + this.ensureCanManage(user); + await this.findOne(provinceId); + const city = this.citiesRepository.create({ + ...createCityDto, + provinceId, + }); + const saved = await this.citiesRepository.save(city); + return this.findCity(provinceId, saved.id); + } + + async findCities(provinceId: string): Promise { + await this.ensureProvinceExists(provinceId); + return this.citiesRepository.find({ + where: { provinceId }, + order: { name: 'ASC' }, + }); + } + + async findCity(provinceId: string, cityId: string): Promise { + const city = await this.citiesRepository.findOne({ + where: { id: cityId, provinceId }, + relations: { province: true }, + }); + if (!city) { + throw new NotFoundException( + `City #${cityId} not found for province #${provinceId}`, + ); + } + return city; + } + + async updateCity( + provinceId: string, + cityId: string, + updateCityDto: UpdateCityDto, + user: AuthUser, + ): Promise { + this.ensureCanManage(user); + const city = await this.findCity(provinceId, cityId); + Object.assign(city, updateCityDto); + await this.citiesRepository.save(city); + return this.findCity(provinceId, cityId); + } + + async removeCity( + provinceId: string, + cityId: string, + user: AuthUser, + ): Promise { + this.ensureCanManage(user); + const city = await this.findCity(provinceId, cityId); + await this.citiesRepository.remove(city); + } + + private ensureCanManage(user: AuthUser): void { + if (!this.provincePolicy.canManage(user)) { + throw new ForbiddenException(); + } + } + + private async ensureProvinceExists(provinceId: string): Promise { + const exists = await this.provincesRepository.existsBy({ id: provinceId }); + if (!exists) { + throw new NotFoundException(`Province #${provinceId} not found`); + } + } +} diff --git a/src/salons/dto/create-salon.dto.ts b/src/salons/dto/create-salon.dto.ts index 662f11b..ded4ae2 100644 --- a/src/salons/dto/create-salon.dto.ts +++ b/src/salons/dto/create-salon.dto.ts @@ -25,6 +25,12 @@ export class CreateSalonDto { @Matches(/^09\d{9}$/) phone: string; + @IsUUID() + provinceId: string; + + @IsUUID() + cityId: string; + @IsOptional() @IsUUID() ownerId?: string; diff --git a/src/salons/dto/update-salon.dto.ts b/src/salons/dto/update-salon.dto.ts index 5ba2710..012871a 100644 --- a/src/salons/dto/update-salon.dto.ts +++ b/src/salons/dto/update-salon.dto.ts @@ -1,4 +1,4 @@ -import { IsOptional, IsString, Length, Matches } from 'class-validator'; +import { IsOptional, IsString, IsUUID, Length, Matches } from 'class-validator'; export class UpdateSalonDto { @IsOptional() @@ -18,4 +18,12 @@ export class UpdateSalonDto { @IsOptional() @Matches(/^09\d{9}$/) phone?: string; + + @IsOptional() + @IsUUID() + provinceId?: string; + + @IsOptional() + @IsUUID() + cityId?: string; } diff --git a/src/salons/entities/salon.entity.ts b/src/salons/entities/salon.entity.ts index 28add99..27bcf70 100644 --- a/src/salons/entities/salon.entity.ts +++ b/src/salons/entities/salon.entity.ts @@ -7,6 +7,8 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; +import { City } from '../../provinces/entities/city.entity'; +import { Province } from '../../provinces/entities/province.entity'; import { User } from '../../users/entities/user.entity'; @Entity('salons') @@ -33,6 +35,20 @@ export class Salon { @JoinColumn({ name: 'ownerId' }) owner: User; + @Column({ nullable: true }) + provinceId: string | null; + + @ManyToOne(() => Province, { onDelete: 'SET NULL', nullable: true }) + @JoinColumn({ name: 'provinceId' }) + province: Province | null; + + @Column({ nullable: true }) + cityId: string | null; + + @ManyToOne(() => City, { onDelete: 'SET NULL', nullable: true }) + @JoinColumn({ name: 'cityId' }) + city: City | null; + @CreateDateColumn() createdAt: Date; diff --git a/src/salons/salons.module.ts b/src/salons/salons.module.ts index b115bc6..f612f29 100644 --- a/src/salons/salons.module.ts +++ b/src/salons/salons.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthGuardsModule } from '../auth/auth-guards.module'; import { AuthorizationModule } from '../authorization/authorization.module'; +import { ProvincesModule } from '../provinces/provinces.module'; import { UsersModule } from '../users/users.module'; import { Salon } from './entities/salon.entity'; import { SalonsController } from './salons.controller'; @@ -11,6 +12,7 @@ import { SalonsService } from './salons.service'; imports: [ TypeOrmModule.forFeature([Salon]), UsersModule, + ProvincesModule, AuthorizationModule, AuthGuardsModule, ], diff --git a/src/salons/salons.service.ts b/src/salons/salons.service.ts index 731583b..f07d5ff 100644 --- a/src/salons/salons.service.ts +++ b/src/salons/salons.service.ts @@ -7,6 +7,7 @@ import { import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { SalonPolicy } from '../authorization/policies/salon.policy'; +import { ProvincesService } from '../provinces/provinces.service'; import { AuthUser } from '../auth/interfaces/auth-user.interface'; import { UserRole } from '../users/enums/user-role.enum'; import { UsersService } from '../users/users.service'; @@ -21,11 +22,16 @@ export class SalonsService { private readonly salonsRepository: Repository, private readonly usersService: UsersService, private readonly salonPolicy: SalonPolicy, + private readonly provincesService: ProvincesService, ) {} async create(createSalonDto: CreateSalonDto, user: AuthUser): Promise { const ownerId = this.resolveOwnerId(createSalonDto, user); await this.ensureOwnerIsSalonOwner(ownerId); + await this.provincesService.findCity( + createSalonDto.provinceId, + createSalonDto.cityId, + ); const salon = this.salonsRepository.create({ ...createSalonDto, @@ -38,17 +44,19 @@ export class SalonsService { if (user?.role === UserRole.SALON) { return this.salonsRepository.find({ where: { ownerId: user.id }, - relations: { owner: true }, + relations: { owner: true, province: true, city: true }, }); } - return this.salonsRepository.find({ relations: { owner: true } }); + return this.salonsRepository.find({ + relations: { owner: true, province: true, city: true }, + }); } async findOne(id: string): Promise { const salon = await this.salonsRepository.findOne({ where: { id }, - relations: { owner: true }, + relations: { owner: true, province: true, city: true }, }); if (!salon) { throw new NotFoundException(`Salon #${id} not found`); @@ -65,6 +73,18 @@ export class SalonsService { if (!this.salonPolicy.canUpdate(user, salon)) { throw new ForbiddenException(); } + + if (updateSalonDto.provinceId && updateSalonDto.cityId) { + await this.provincesService.findCity( + updateSalonDto.provinceId, + updateSalonDto.cityId, + ); + } else if (updateSalonDto.provinceId || updateSalonDto.cityId) { + throw new BadRequestException( + 'Both provinceId and cityId are required when updating location', + ); + } + Object.assign(salon, updateSalonDto); return this.salonsRepository.save(salon); } diff --git a/tsconfig.json b/tsconfig.json index aba29b0..c7f9adf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,7 @@ "forceConsistentCasingInFileNames": true, "noImplicitAny": false, "strictBindCallApply": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": false, + "resolveJsonModule": true } }