diff --git a/src/card/services/card.service.ts b/src/card/services/card.service.ts index 5138824..5be8fbc 100644 --- a/src/card/services/card.service.ts +++ b/src/card/services/card.service.ts @@ -42,13 +42,11 @@ export class CardService { throw new BadRequestException('CUSTOMER.ALREADY_HAS_CARD'); } - // Validate required address fields for card creation + // Validate required fields for card creation const missingFields = []; - if (!customer.country) missingFields.push('country'); - if (!customer.city) missingFields.push('city'); - if (!customer.region) missingFields.push('region'); if (!customer.nationalId) missingFields.push('nationalId'); if (!customer.dateOfBirth) missingFields.push('dateOfBirth'); + if (!customer.nationalIdExpiry) missingFields.push('nationalIdExpiry'); if (missingFields.length > 0) { throw new BadRequestException( diff --git a/src/common/modules/neoleap/services/neoleap.service.ts b/src/common/modules/neoleap/services/neoleap.service.ts index 463a72f..443c604 100644 --- a/src/common/modules/neoleap/services/neoleap.service.ts +++ b/src/common/modules/neoleap/services/neoleap.service.ts @@ -93,24 +93,22 @@ export class NeoLeapService { incomeSource: dto.incomeSource, jobCategory: dto.jobCategory, incomeRange: dto.incomeRange, - // Map collected address fields to Neoleap's format + // Use default address values for Neoleap KYC address: { national: { - buildingNumber: dto.building || '', + buildingNumber: '1', additionalNumber: '', - street: dto.street || '', - streetEn: dto.street || '', - city: dto.city || '', - cityEn: dto.city || '', + street: 'King Fahd Road', + streetEn: 'King Fahd Road', + city: 'Riyadh', + cityEn: 'Riyadh', zipcode: '', unitNumber: '', - district: dto.neighborhood || '', - districtEn: dto.neighborhood || '', + district: 'Al Olaya', + districtEn: 'Al Olaya', }, general: { - address: [dto.building, dto.street, dto.neighborhood, dto.city, dto.region] - .filter(Boolean) - .join(', '), + address: '1, King Fahd Road, Al Olaya, Riyadh, Riyadh', website: '', email: dto.email || '', telephone1: dto.mobileNumber || '', @@ -203,14 +201,14 @@ export class NeoLeapService { Title: customer.gender === Gender.MALE ? 'Mr' : 'Ms', Gender: customer.gender === Gender.MALE ? 'M' : 'F', LocalizedDateOfBirth: moment(customer.dateOfBirth).format('YYYY-MM-DD'), - Nationality: CountriesNumericISO[customer.countryOfResidence], + Nationality: CountriesNumericISO[customer.countryOfResidence || 'SA'], }, ApplicationAddress: { - City: customer.city, - Country: CountriesNumericISO[customer.country], - Region: customer.region, - AddressLine1: `${customer.street} ${customer.building}`, - AddressLine2: customer.neighborhood, + City: 'Riyadh', + Country: CountriesNumericISO['SA'], + Region: 'Riyadh', + AddressLine1: 'King Fahd Road 1', + AddressLine2: 'Al Olaya', AddressRole: 0, Email: customer.user.email, Phone1: customer.user.phoneNumber, @@ -279,14 +277,14 @@ export class NeoLeapService { Title: parent.gender === Gender.MALE ? 'Mr' : 'Ms', Gender: parent.gender === Gender.MALE ? 'M' : 'F', LocalizedDateOfBirth: moment(parent.dateOfBirth).format('YYYY-MM-DD'), - Nationality: CountriesNumericISO[parent.countryOfResidence], + Nationality: CountriesNumericISO[parent.countryOfResidence || 'SA'], }, ApplicationAddress: { - City: parent.city, - Country: CountriesNumericISO[parent.country], - Region: parent.region, - AddressLine1: `${parent.street} ${parent.building}`, - AddressLine2: parent.neighborhood, + City: 'Riyadh', + Country: CountriesNumericISO['SA'], + Region: 'Riyadh', + AddressLine1: 'King Fahd Road 1', + AddressLine2: 'Al Olaya', AddressRole: 0, Email: child.user.email, Phone1: child.user.phoneNumber, diff --git a/src/customer/dtos/request/initiate-kyc.request.dto.ts b/src/customer/dtos/request/initiate-kyc.request.dto.ts index 65a2171..20ed1a7 100644 --- a/src/customer/dtos/request/initiate-kyc.request.dto.ts +++ b/src/customer/dtos/request/initiate-kyc.request.dto.ts @@ -1,8 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsDateString, IsEmail, IsEnum, IsOptional, IsString, Matches } from 'class-validator'; import { i18nValidationMessage as i18n } from 'nestjs-i18n'; -import { CountryIso } from '~/common/enums'; -import { IncomeRange, IncomeSource, JobCategory, JobSector, PoiType } from '~/customer/enums'; +import { Gender, IncomeRange, IncomeSource, JobCategory, JobSector, PoiType } from '~/customer/enums'; export class InitiateKycRequestDto { @ApiProperty({ example: '2586234623', description: 'Saudi National ID or Iqama number' }) @@ -26,6 +25,14 @@ export class InitiateKycRequestDto { @IsDateString({}, { message: i18n('validation.IsDateString', { path: 'general', property: 'customer.dateOfBirth' }) }) dateOfBirth!: string; + @ApiProperty({ example: '2030-12-31', format: 'date', description: 'National ID expiry date' }) + @IsDateString({}, { message: i18n('validation.IsDateString', { path: 'general', property: 'customer.nationalIdExpiry' }) }) + nationalIdExpiry!: string; + + @ApiProperty({ enum: Gender, example: Gender.MALE }) + @IsEnum(Gender, { message: i18n('validation.IsEnum', { path: 'general', property: 'customer.gender' }) }) + gender!: Gender; + @ApiProperty({ enum: JobSector, example: JobSector.PRIVATE_SECTOR }) @IsEnum(JobSector, { message: i18n('validation.IsEnum', { path: 'general', property: 'customer.jobSector' }) }) jobSector!: JobSector; @@ -45,37 +52,4 @@ export class InitiateKycRequestDto { @ApiProperty({ enum: IncomeRange, example: IncomeRange.RANGE_10000_20000 }) @IsEnum(IncomeRange, { message: i18n('validation.IsEnum', { path: 'general', property: 'customer.incomeRange' }) }) incomeRange!: IncomeRange; - - // Address fields (optional - can be provided here or during registration) - @ApiProperty({ example: 'SA', description: 'Country code', required: false }) - @IsEnum(CountryIso, { - message: i18n('validation.IsEnum', { path: 'general', property: 'customer.country' }), - }) - @IsOptional() - country?: CountryIso; - - @ApiProperty({ example: 'Riyadh', description: 'Region/Province', required: false }) - @IsString({ message: i18n('validation.IsString', { path: 'general', property: 'customer.region' }) }) - @IsOptional() - region?: string; - - @ApiProperty({ example: 'Riyadh', description: 'City', required: false }) - @IsString({ message: i18n('validation.IsString', { path: 'general', property: 'customer.city' }) }) - @IsOptional() - city?: string; - - @ApiProperty({ example: 'Al Olaya', description: 'Neighborhood/District', required: false }) - @IsString({ message: i18n('validation.IsString', { path: 'general', property: 'customer.neighborhood' }) }) - @IsOptional() - neighborhood?: string; - - @ApiProperty({ example: 'King Fahd Road', description: 'Street name', required: false }) - @IsString({ message: i18n('validation.IsString', { path: 'general', property: 'customer.street' }) }) - @IsOptional() - street?: string; - - @ApiProperty({ example: '123', description: 'Building number', required: false }) - @IsString({ message: i18n('validation.IsString', { path: 'general', property: 'customer.building' }) }) - @IsOptional() - building?: string; } diff --git a/src/customer/dtos/response/customer.response.dto.ts b/src/customer/dtos/response/customer.response.dto.ts index 3ed0423..9e02c86 100644 --- a/src/customer/dtos/response/customer.response.dto.ts +++ b/src/customer/dtos/response/customer.response.dto.ts @@ -49,24 +49,6 @@ export class CustomerResponseDto { @ApiProperty({ example: 12345 }) waitingNumber!: number; - @ApiProperty({ example: 'SA' }) - country!: string | null; - - @ApiProperty({ example: 'Riyadh' }) - region!: string | null; - - @ApiProperty({ example: 'Riyadh City' }) - city!: string | null; - - @ApiProperty({ example: 'Al-Masif' }) - neighborhood!: string | null; - - @ApiProperty({ example: 'King Fahd Road' }) - street!: string | null; - - @ApiProperty({ example: '123' }) - building!: string | null; - @ApiPropertyOptional({ type: DocumentMetaResponseDto }) profilePicture!: DocumentMetaResponseDto | null; @@ -86,11 +68,5 @@ export class CustomerResponseDto { this.isJunior = customer.isJunior; this.isGuardian = customer.isGuardian; this.waitingNumber = customer.applicationNumber; - this.country = customer.country; - this.region = customer.region; - this.city = customer.city; - this.neighborhood = customer.neighborhood; - this.street = customer.street; - this.building = customer.building; } } diff --git a/src/customer/entities/customer.entity.ts b/src/customer/entities/customer.entity.ts index ae381ae..772dc11 100644 --- a/src/customer/entities/customer.entity.ts +++ b/src/customer/entities/customer.entity.ts @@ -68,24 +68,6 @@ export class Customer extends BaseEntity { @Column('varchar', { name: 'user_id' }) userId!: string; - @Column('varchar', { name: 'country', length: 255, nullable: true }) - country!: CountryIso; - - @Column('varchar', { name: 'region', length: 255, nullable: true }) - region!: string; - - @Column('varchar', { name: 'city', length: 255, nullable: true }) - city!: string; - - @Column('varchar', { name: 'neighborhood', length: 255, nullable: true }) - neighborhood!: string; - - @Column('varchar', { name: 'street', length: 255, nullable: true }) - street!: string; - - @Column('varchar', { name: 'building', length: 255, nullable: true }) - building!: string; - // KYC-specific fields @Column('varchar', { length: 255, nullable: true, name: 'neoleap_external_customer_id' }) neoleapExternalCustomerId!: string | null; diff --git a/src/customer/repositories/customer.repository.ts b/src/customer/repositories/customer.repository.ts index 6bb35e3..276a6d4 100644 --- a/src/customer/repositories/customer.repository.ts +++ b/src/customer/repositories/customer.repository.ts @@ -30,13 +30,6 @@ export class CustomerRepository { dateOfBirth: body.dateOfBirth, countryOfResidence: body.countryOfResidence, gender: body.gender, - // Address fields - country: body.country, - region: body.region, - city: body.city, - neighborhood: body.neighborhood, - street: body.street, - building: body.building, }), ); } diff --git a/src/customer/services/customer.service.ts b/src/customer/services/customer.service.ts index 5fc67fb..d47ab97 100644 --- a/src/customer/services/customer.service.ts +++ b/src/customer/services/customer.service.ts @@ -73,10 +73,13 @@ export class CustomerService { throw new ConflictException('KYC verification already in progress for this National ID'); } - // Update customer with KYC data (including address if provided) + // Update customer with KYC data await this.customerRepository.updateCustomer(customerId, { nationalId: body.poiNumber, dateOfBirth: new Date(body.dateOfBirth), + nationalIdExpiry: new Date(body.nationalIdExpiry), + gender: body.gender, + countryOfResidence: CountryIso.SAUDI_ARABIA, // Always default to Saudi Arabia mobileNumber: body.mobileNumber, jobSector: body.jobSector, employer: body.employer, @@ -84,13 +87,6 @@ export class CustomerService { jobCategory: body.jobCategory, incomeRange: body.incomeRange, kycStatus: KycStatus.PENDING, - // Address fields (only update if provided) - ...(body.country && { country: body.country }), - ...(body.region && { region: body.region }), - ...(body.city && { city: body.city }), - ...(body.neighborhood && { neighborhood: body.neighborhood }), - ...(body.street && { street: body.street }), - ...(body.building && { building: body.building }), }); // Call Neoleap KYC API @@ -184,12 +180,6 @@ export class CustomerService { nationalId: '1089055972', nationalIdExpiry: moment('2031-09-17').toDate(), countryOfResidence: CountryIso.SAUDI_ARABIA, - country: CountryIso.SAUDI_ARABIA, - region: 'Mecca', - city: 'AT Taif', - neighborhood: 'Al Faisaliah', - street: 'Al Faisaliah Street', - building: '4', }); await User.update(userId, { diff --git a/src/db/migrations/1765975126402-RemoveAddressColumns.ts b/src/db/migrations/1765975126402-RemoveAddressColumns.ts new file mode 100644 index 0000000..36d8b62 --- /dev/null +++ b/src/db/migrations/1765975126402-RemoveAddressColumns.ts @@ -0,0 +1,26 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveAddressColumns1765975126402 implements MigrationInterface { + name = 'RemoveAddressColumns1765975126402'; + + public async up(queryRunner: QueryRunner): Promise { + // Drop address columns from customers table + await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN IF EXISTS "country"`); + await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN IF EXISTS "region"`); + await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN IF EXISTS "city"`); + await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN IF EXISTS "neighborhood"`); + await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN IF EXISTS "street"`); + await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN IF EXISTS "building"`); + } + + public async down(queryRunner: QueryRunner): Promise { + // Re-add address columns in case of rollback + await queryRunner.query(`ALTER TABLE "customers" ADD "country" varchar(255)`); + await queryRunner.query(`ALTER TABLE "customers" ADD "region" varchar(255)`); + await queryRunner.query(`ALTER TABLE "customers" ADD "city" varchar(255)`); + await queryRunner.query(`ALTER TABLE "customers" ADD "neighborhood" varchar(255)`); + await queryRunner.query(`ALTER TABLE "customers" ADD "street" varchar(255)`); + await queryRunner.query(`ALTER TABLE "customers" ADD "building" varchar(255)`); + } +} + diff --git a/src/db/migrations/index.ts b/src/db/migrations/index.ts index 9816012..a14bef0 100644 --- a/src/db/migrations/index.ts +++ b/src/db/migrations/index.ts @@ -8,4 +8,5 @@ export * from './1760869651296-AddMerchantInfoToTransactions'; export * from './1761032305682-AddUniqueConstraintToUserEmail'; export * from './1765804942393-AddKycFieldsAndTransactions'; export * from './1765877128065-AddNationalIdToKycTransactions'; -export * from './1765891028260-RemoveOldCustomerColumns'; \ No newline at end of file +export * from './1765891028260-RemoveOldCustomerColumns'; +export * from './1765975126402-RemoveAddressColumns'; \ No newline at end of file diff --git a/src/user/services/user.service.ts b/src/user/services/user.service.ts index 075af6c..cc7aa12 100644 --- a/src/user/services/user.service.ts +++ b/src/user/services/user.service.ts @@ -65,13 +65,6 @@ export class UserService { firstName: body.firstName, lastName: body.lastName, countryOfResidence: body.countryOfResidence, - // Address fields (optional during registration) - country: body.country, - region: body.region, - city: body.city, - neighborhood: body.neighborhood, - street: body.street, - building: body.building, }), this.userRepository.update(userId, { isPhoneVerified: true,