Compare commits

...

16 Commits

Author SHA1 Message Date
9d9408dedd Merge pull request #49 from HamzaSha1/ZOD-349-weekly-spending-data-not-displaying-in-the-child-profile-parent-view
Zod 349 weekly spending data not displaying in the child profile parent view
2025-10-26 13:15:39 +03:00
bbeece9e03 git checkout -b ZOD-349-weekly-spending-data-not-displaying-in-the-child-profile-parent-view 2025-10-26 13:14:35 +03:00
596562f6dc Merge pull request #48 from HamzaSha1/feat/neoleap-integration
Feat/neoleap integration
2025-10-21 14:56:38 +03:00
10de8f69c9 Merge pull request #47 from HamzaSha1/ZOD-341-junior-a-child-can-edit-their-email-to-an-existing-email-causing-multiple-child-accounts-to-share-the-same-login
Remove duplicate email cleanup logic and add unique constraint to use…
2025-10-21 14:15:03 +03:00
8a6b1cc900 Remove duplicate email cleanup logic and add unique constraint to user email 2025-10-21 14:10:14 +03:00
d16ae66252 Merge pull request #46 from HamzaSha1/ZOD-341-junior-a-child-can-edit-their-email-to-an-existing-email-causing-multiple-child-accounts-to-share-the-same-login
ZOD-341-Add unique constraint to user email and clean up duplicates
2025-10-21 10:51:12 +03:00
e966f95463 ZOD-341-Add unique constraint to user email and clean up duplicates 2025-10-21 10:49:43 +03:00
2714255dd1 Merge pull request #45 from HamzaSha1/ZOD-341-junior-a-child-can-edit-their-email-to-an-existing-email-causing-multiple-child-accounts-to-share-the-same-login
ZOD-341 Add email uniqueness validation to prevent duplicate emails
2025-10-20 14:31:11 +03:00
39a0b131b8 Merge pull request #44 from HamzaSha1/ZOD-341-junior-a-child-can-edit-their-email-to-an-existing-email-causing-multiple-child-accounts-to-share-the-same-login
Zod 341 junior a child can edit their email to an existing email causing multiple child accounts to share the same login
2025-10-20 14:27:40 +03:00
4f778f7904 * ZOD-341-junior-a-child-can-edit-their-email-to-an-existing-email-causing-multiple-child-accounts-to-share-the-same-login 2025-10-20 14:25:53 +03:00
7e9bc397a9 Merge pull request #43 from HamzaSha1/ZOD-204-view-spending-from-child-login
git checkout -b ZOD-204-view-spending-from-child-login
2025-10-20 10:30:27 +03:00
7bfc14f0d9 Merge pull request #42 from HamzaSha1/ZOD-204-view-spending-from-child-login
ZOD-204-view-spending-from-child-login
2025-10-19 15:44:16 +03:00
f81714a525 Merge pull request #41 from HamzaSha1/ZOD-339-child-profile-gender-update-is-not-reflected-after-editing
ZOD-339-child-profile-gender-update-is-not-reflected-after-editing
2025-10-19 11:07:39 +03:00
f3282a680b Merge pull request #40 from HamzaSha1/ZOD-339-child-profile-gender-update-is-not-reflected-after-editing
Zod 339 child profile gender update is not reflected after editing
2025-10-19 11:02:40 +03:00
7b57277a7f ZOD-339-child-profile-gender-update-is-not-reflected-after-editing 2025-10-19 11:01:52 +03:00
d70ab09960 Merge pull request #38 from HamzaSha1/ZOD-333-junior-incorrect-relationship-label-displayed-as-child-instead-of-daughter-or-son-in-child-confirmation-details-after-the-scan-the-qr-code
Zod 333 junior incorrect relationship label displayed as child instead of daughter or son in child confirmation details after the scan the qr code
2025-10-19 09:58:57 +03:00
7 changed files with 65 additions and 10 deletions

View File

@ -85,8 +85,8 @@ export class TransactionService {
}
async getWeeklySummary(juniorId: string) {
const startOfWeek = moment().startOf('week').toDate();
const endOfWeek = moment().endOf('week').toDate();
const endOfWeek = moment().toDate();
const startOfWeek = moment().subtract(1, 'week').toDate();
const transactions = await this.transactionRepository.getTransactionsForCardWithinDateRange(
juniorId,

View File

@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class AddUniqueConstraintToUserEmail1761032305682 implements MigrationInterface {
name = 'AddUniqueConstraintToUserEmail1761032305682'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email")`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3"`);
}
}

View File

@ -4,4 +4,5 @@ export * from './1754915164810-seed-default-avatar';
export * from './1757349525708-create-money-requests-table';
export * from './1757433339849-add-reservation-amount-to-account-entity';
export * from './1757915357218-add-deleted-at-column-to-junior';
export * from './1760869651296-AddMerchantInfoToTransactions';
export * from './1760869651296-AddMerchantInfoToTransactions';
export * from './1761032305682-AddUniqueConstraintToUserEmail';

View File

@ -120,6 +120,7 @@ export class JuniorService {
setIf(customer, 'firstName', body.firstName);
setIf(customer, 'lastName', body.lastName);
setIf(customer, 'dateOfBirth', body.dateOfBirth as unknown as Date);
setIf(customer, 'gender', body.gender);
setIf(junior, 'relationship', body.relationship);
await Promise.all([junior.save(), customer.save(), user.save()]);
@ -211,8 +212,8 @@ export class JuniorService {
this.logger.log(`Junior ${juniorId} deleted successfully`);
}
getWeeklySummary(juniorId: string, guardianId: string) {
const doesBelong = this.doesJuniorBelongToGuardian(guardianId, juniorId);
async getWeeklySummary(juniorId: string, guardianId: string) {
const doesBelong = await this.doesJuniorBelongToGuardian(guardianId, juniorId);
if (!doesBelong) {
this.logger.error(`Junior ${juniorId} does not belong to guardian ${guardianId}`);

View File

@ -1,6 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsDateString, IsEmail, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
import { i18nValidationMessage as i18n } from 'nestjs-i18n';
import { Gender } from '~/customer/enums';
export class UpdateUserRequestDto {
@ApiProperty({ example: 'John' })
@IsString({ message: i18n('validation.IsString', { path: 'general', property: 'user.firstName' }) })
@ -14,8 +15,23 @@ export class UpdateUserRequestDto {
@IsOptional()
lastName!: string;
@ApiPropertyOptional({ example: 'child@example.com' })
@IsEmail({}, { message: i18n('validation.IsEmail', { path: 'general', property: 'user.email' }) })
@IsOptional()
email!: string;
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000' })
@IsUUID('4', { message: i18n('validation.IsUUID', { path: 'general', property: 'user.profilePictureId' }) })
@IsOptional()
profilePictureId!: string;
@ApiPropertyOptional({ enum: Gender })
@IsEnum(Gender, { message: i18n('validation.IsEnum', { path: 'general', property: 'customer.gender' }) })
@IsOptional()
gender!: Gender;
@ApiPropertyOptional({ example: '2020-01-01' })
@IsDateString({}, { message: i18n('validation.IsDateString', { path: 'general', property: 'customer.dateOfBirth' }) })
@IsOptional()
dateOfBirth!: Date;
}

View File

@ -28,7 +28,7 @@ export class User extends BaseEntity {
@Column('varchar', { length: 255, name: 'last_name', nullable: false })
lastName!: string;
@Column('varchar', { length: 255, name: 'email', nullable: true })
@Column('varchar', { length: 255, name: 'email', nullable: true, unique: true })
email!: string;
@Column('varchar', { length: 255, name: 'phone_number', nullable: true })

View File

@ -191,16 +191,38 @@ export class UserService {
async updateUser(userId: string, data: UpdateUserRequestDto) {
await this.validateProfilePictureId(data.profilePictureId, userId);
if (data.email) {
const userWithEmail = await this.findUser({ email: data.email });
if (userWithEmail && userWithEmail.id !== userId) {
this.logger.error(`Email ${data.email} is already taken by another user`);
throw new BadRequestException('USER.EMAIL_ALREADY_TAKEN');
}
}
this.logger.log(`Updating user ${userId} with data ${JSON.stringify(data)}`);
const { affected } = await this.userRepository.update(userId, data);
const { gender, dateOfBirth, ...userData } = data;
const { affected } = await this.userRepository.update(userId, userData);
if (affected === 0) {
this.logger.error(`User with id ${userId} not found`);
throw new BadRequestException('USER.NOT_FOUND');
}
if (gender !== undefined || dateOfBirth !== undefined) {
const customerData: Partial<{ gender: typeof gender; dateOfBirth: Date }> = {};
if (gender !== undefined) {
customerData.gender = gender;
}
if (dateOfBirth !== undefined) {
customerData.dateOfBirth = dateOfBirth;
}
await this.customerService.updateCustomer(userId, customerData);
}
}
async updateUserEmail(userId: string, email: string) {
const userWithEmail = await this.findUser({ email, isEmailVerified: true });
const userWithEmail = await this.findUser({ email });
if (userWithEmail) {
if (userWithEmail.id === userId) {