From 804f478f5dec660677724b1441ab3db6e1159fb6 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Mar 2024 22:54:29 +0530 Subject: [PATCH 1/8] fix: organization contact typeorm repository --- .../organization-contact.entity.ts | 39 +++++++++++-------- .../organization-contact.module.ts | 5 ++- .../organization-contact/repository/index.ts | 2 + ...ype-orm-organization-contact.repository.ts | 9 ++++- ...organization-belongs-to-user.constraint.ts | 3 +- 5 files changed, 36 insertions(+), 22 deletions(-) create mode 100644 packages/core/src/organization-contact/repository/index.ts diff --git a/packages/core/src/organization-contact/organization-contact.entity.ts b/packages/core/src/organization-contact/organization-contact.entity.ts index 328cf0d5d9b..718563c21ef 100644 --- a/packages/core/src/organization-contact/organization-contact.entity.ts +++ b/packages/core/src/organization-contact/organization-contact.entity.ts @@ -3,6 +3,7 @@ import { JoinTable, RelationId } from 'typeorm'; +import { EntityRepositoryType } from '@mikro-orm/core'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsOptional, IsUUID } from 'class-validator'; import { @@ -35,12 +36,21 @@ import { TenantOrganizationBaseEntity, TimeLog } from '../core/entities/internal'; -import { ColumnIndex, MultiORMColumn, MultiORMEntity, MultiORMManyToMany, MultiORMManyToOne, MultiORMOneToMany, MultiORMOneToOne } from './../core/decorators/entity'; +import { + ColumnIndex, + MultiORMColumn, + MultiORMEntity, + MultiORMManyToMany, + MultiORMManyToOne, + MultiORMOneToMany, + MultiORMOneToOne +} from './../core/decorators/entity'; import { MikroOrmOrganizationContactRepository } from './repository/mikro-orm-organization-contact.repository'; @MultiORMEntity('organization_contact', { mikroOrmRepository: () => MikroOrmOrganizationContactRepository }) -export class OrganizationContact extends TenantOrganizationBaseEntity - implements IOrganizationContact { +export class OrganizationContact extends TenantOrganizationBaseEntity implements IOrganizationContact { + + [EntityRepositoryType]?: MikroOrmOrganizationContactRepository; @ApiProperty({ type: () => String }) @ColumnIndex() @@ -56,11 +66,7 @@ export class OrganizationContact extends TenantOrganizationBaseEntity primaryPhone: string; @ApiProperty({ type: () => String, enum: ContactOrganizationInviteStatus }) - @MultiORMColumn({ - type: 'simple-enum', - nullable: true, - enum: ContactOrganizationInviteStatus - }) + @MultiORMColumn({ type: 'simple-enum', nullable: true, enum: ContactOrganizationInviteStatus }) inviteStatus?: ContactOrganizationInviteStatus; @ApiPropertyOptional({ type: () => String }) @@ -68,12 +74,7 @@ export class OrganizationContact extends TenantOrganizationBaseEntity notes?: string; @ApiProperty({ type: () => String, enum: ContactType }) - @MultiORMColumn({ - type: 'simple-enum', - nullable: false, - enum: ContactType, - default: ContactType.CLIENT - }) + @MultiORMColumn({ type: 'simple-enum', enum: ContactType, default: ContactType.CLIENT }) contactType: ContactType; @ApiPropertyOptional({ type: () => String, maxLength: 500 }) @@ -108,16 +109,19 @@ export class OrganizationContact extends TenantOrganizationBaseEntity */ @ApiProperty({ type: () => Contact }) @MultiORMOneToOne(() => Contact, (contact) => contact.organizationContact, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, - /** Database cascade action on delete. */ onDelete: 'SET NULL', + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() contact?: IContact; - @ApiProperty({ type: () => String }) + @ApiPropertyOptional({ type: () => String }) @IsOptional() @IsUUID() @RelationId((it: OrganizationContact) => it.contact) @@ -129,9 +133,10 @@ export class OrganizationContact extends TenantOrganizationBaseEntity * ImageAsset */ @MultiORMManyToOne(() => ImageAsset, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, /** Database cascade action on delete. */ onDelete: 'SET NULL', - /** Eager relations are always loaded automatically when relation's owner entity is loaded using find* methods. */ eager: true }) diff --git a/packages/core/src/organization-contact/organization-contact.module.ts b/packages/core/src/organization-contact/organization-contact.module.ts index dc797e59f68..e694050768b 100644 --- a/packages/core/src/organization-contact/organization-contact.module.ts +++ b/packages/core/src/organization-contact/organization-contact.module.ts @@ -10,6 +10,7 @@ import { CommandHandlers } from './commands/handlers'; import { RolePermissionModule } from '../role-permission/role-permission.module'; import { OrganizationModule } from './../organization/organization.module'; import { OrganizationProjectModule } from './../organization-project/organization-project.module'; +import { TypeOrmOrganizationContactRepository } from './repository'; @Module({ imports: [ @@ -27,7 +28,7 @@ import { OrganizationProjectModule } from './../organization-project/organizatio CqrsModule ], controllers: [OrganizationContactController], - providers: [OrganizationContactService, ...CommandHandlers], - exports: [TypeOrmModule, MikroOrmModule, OrganizationContactService] + providers: [OrganizationContactService, TypeOrmOrganizationContactRepository, ...CommandHandlers], + exports: [TypeOrmModule, MikroOrmModule, OrganizationContactService, TypeOrmOrganizationContactRepository] }) export class OrganizationContactModule { } diff --git a/packages/core/src/organization-contact/repository/index.ts b/packages/core/src/organization-contact/repository/index.ts new file mode 100644 index 00000000000..3d9036fc8e9 --- /dev/null +++ b/packages/core/src/organization-contact/repository/index.ts @@ -0,0 +1,2 @@ +export * from './mikro-orm-organization-contact.repository'; +export * from './type-orm-organization-contact.repository'; diff --git a/packages/core/src/organization-contact/repository/type-orm-organization-contact.repository.ts b/packages/core/src/organization-contact/repository/type-orm-organization-contact.repository.ts index fe5d68ee71d..3ff68067ae5 100644 --- a/packages/core/src/organization-contact/repository/type-orm-organization-contact.repository.ts +++ b/packages/core/src/organization-contact/repository/type-orm-organization-contact.repository.ts @@ -1,4 +1,11 @@ +import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { OrganizationContact } from '../organization-contact.entity'; -export class TypeOrmOrganizationContactRepository extends Repository { } \ No newline at end of file +export class TypeOrmOrganizationContactRepository extends Repository { + constructor( + @InjectRepository(OrganizationContact) readonly repository: Repository + ) { + super(repository.target, repository.manager, repository.queryRunner); + } +} diff --git a/packages/core/src/shared/validators/constraints/organization-belongs-to-user.constraint.ts b/packages/core/src/shared/validators/constraints/organization-belongs-to-user.constraint.ts index 1382ccc2c0c..a4b31a116f4 100644 --- a/packages/core/src/shared/validators/constraints/organization-belongs-to-user.constraint.ts +++ b/packages/core/src/shared/validators/constraints/organization-belongs-to-user.constraint.ts @@ -9,8 +9,7 @@ import { IOrganization } from "@gauzy/contracts"; import { isEmpty } from "@gauzy/common"; import { UserOrganization } from "../../../core/entities/internal"; import { RequestContext } from "../../../core/context"; -import { TypeOrmUserOrganizationRepository } from "../../../user-organization/repository/type-orm-user-organization.repository"; -import { MikroOrmUserOrganizationRepository } from "../../../user-organization/repository/mikro-orm-user-organization.repository"; +import { MikroOrmUserOrganizationRepository, TypeOrmUserOrganizationRepository } from "../../../user-organization/repository"; /** * Validator constraint for checking if a user belongs to the organization. From 7741c86e47c58d1b853951db3ae3ac3b44ba3c48 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 19 Mar 2024 23:10:56 +0530 Subject: [PATCH 2/8] fix: organization contact create/list for mikro orm --- .../contact-select.component.ts | 65 +++++++++++-------- packages/contracts/src/base-entity.model.ts | 13 ++++ packages/contracts/src/contact.model.ts | 9 ++- .../src/organization-contact.model.ts | 12 ++-- packages/core/src/core/utils.ts | 2 +- packages/core/src/employee/employee.entity.ts | 8 +-- .../organization-contact-create.handler.ts | 63 ++++++++++-------- .../organization-contact-update.handler.ts | 20 ++++-- 8 files changed, 115 insertions(+), 77 deletions(-) diff --git a/apps/gauzy/src/app/@shared/contact-select/contact-select.component.ts b/apps/gauzy/src/app/@shared/contact-select/contact-select.component.ts index c65813a59dd..392f4ee2e83 100644 --- a/apps/gauzy/src/app/@shared/contact-select/contact-select.component.ts +++ b/apps/gauzy/src/app/@shared/contact-select/contact-select.component.ts @@ -5,7 +5,8 @@ import { Input, forwardRef, EventEmitter, - Output} from '@angular/core'; + Output +} from '@angular/core'; import { ContactType, IOrganization, @@ -32,9 +33,9 @@ import { TranslationBaseComponent } from '../language-base/translation-base.comp } ] }) -export class ContactSelectComponent extends TranslationBaseComponent +export class ContactSelectComponent extends TranslationBaseComponent implements OnInit, OnDestroy { - + public hasEditEmployee$: Observable; contacts: IOrganizationContact[] = []; organization: IOrganization; @@ -94,9 +95,9 @@ export class ContactSelectComponent extends TranslationBaseComponent @Input() set searchable(value: boolean) { this._searchable = value; } - - onChange: any = () => {}; - onTouched: any = () => {}; + + onChange: any = () => { }; + onTouched: any = () => { }; private _organizationContact: IOrganizationContact; set organizationContact(val: IOrganizationContact) { @@ -123,12 +124,12 @@ export class ContactSelectComponent extends TranslationBaseComponent ngOnInit() { this.subject$ - .pipe( - debounceTime(100), - tap(() => this.getContacts()), - untilDestroyed(this) - ) - .subscribe(); + .pipe( + debounceTime(100), + tap(() => this.getContacts()), + untilDestroyed(this) + ) + .subscribe(); this.store.selectedOrganization$ .pipe( filter((organization: IOrganization) => !!organization), @@ -184,32 +185,42 @@ export class ContactSelectComponent extends TranslationBaseComponent } } - addOrganizationContact = ( - name: string - ): Promise => { + /** + * Adds a new organization contact with the specified name. + * + * @param name The name of the contact to add. + * @returns A promise that resolves to the created organization contact object. + */ + addOrganizationContact = async (name: string): Promise => { + if (!this.organization) { + return null; + } + const { tenantId } = this.store.user; const { id: organizationId } = this.organization; try { - this.toastrService.success( - this.getTranslation( - 'NOTES.ORGANIZATIONS.EDIT_ORGANIZATIONS_CONTACTS.ADD_CONTACT', - { - name: name - } - ), - this.getTranslation('TOASTR.TITLE.SUCCESS') - ); - return this.organizationContactService.create({ + const contact = await this.organizationContactService.create({ name, contactType: ContactType.CLIENT, organizationId, - tenantId + organization: { id: organizationId }, + tenantId, + tenant: { id: tenantId } }); + + this.toastrService.success( + this.getTranslation('NOTES.ORGANIZATIONS.EDIT_ORGANIZATIONS_CONTACTS.ADD_CONTACT', { name }), + this.getTranslation('TOASTR.TITLE.SUCCESS') + ); + + return contact; } catch (error) { this.errorHandler.handleError(error); + // Optionally, re-throw or return null to indicate failure + return null; } }; - ngOnDestroy() {} + ngOnDestroy() { } } diff --git a/packages/contracts/src/base-entity.model.ts b/packages/contracts/src/base-entity.model.ts index 24be70f5b31..a6fb340d4c1 100644 --- a/packages/contracts/src/base-entity.model.ts +++ b/packages/contracts/src/base-entity.model.ts @@ -28,8 +28,21 @@ export interface IBasePerTenantEntityModel extends IBaseEntityModel { tenant?: ITenant; // Reference to the associated tenant } +// Mutation input properties for entities associated with a tenant +export interface IBasePerTenantEntityMutationInput extends + Pick, IBaseEntityModel { + tenant?: Pick; +} + // Common properties for entities associated with both tenant and organization export interface IBasePerTenantAndOrganizationEntityModel extends IBasePerTenantEntityModel { organizationId?: IOrganization['id']; // Identifier of the associated organization organization?: IOrganization; // Reference to the associated organization } + +// Mutation input properties for entities associated with both tenant and organization +export interface IBasePerTenantAndOrganizationEntityMutationInput extends + Pick, + Partial { + organization?: Pick; +} diff --git a/packages/contracts/src/contact.model.ts b/packages/contracts/src/contact.model.ts index 1df428ebbc9..42945acf283 100644 --- a/packages/contracts/src/contact.model.ts +++ b/packages/contracts/src/contact.model.ts @@ -1,11 +1,11 @@ -import { IBasePerTenantAndOrganizationEntityModel } from './base-entity.model'; +import { IBasePerTenantAndOrganizationEntityModel, IBasePerTenantAndOrganizationEntityMutationInput } from './base-entity.model'; import { ICandidate } from './candidate.model'; import { IEmployee } from './employee.model'; import { IOrganizationContact } from './organization-contact.model'; export interface IRelationalContact { - readonly contact?: IContact; - readonly contactId?: IContact['id']; + readonly contact?: IContact; + readonly contactId?: IContact['id']; } export interface IContact extends IBasePerTenantAndOrganizationEntityModel { @@ -33,8 +33,7 @@ export interface IContactFindInput extends IContactCreateInput { id?: string; } -export interface IContactCreateInput - extends IBasePerTenantAndOrganizationEntityModel { +export interface IContactCreateInput extends IBasePerTenantAndOrganizationEntityMutationInput { name?: string; firstName?: string; lastName?: string; diff --git a/packages/contracts/src/organization-contact.model.ts b/packages/contracts/src/organization-contact.model.ts index a86ab0f20d4..2bbd10164fd 100644 --- a/packages/contracts/src/organization-contact.model.ts +++ b/packages/contracts/src/organization-contact.model.ts @@ -4,8 +4,8 @@ import { IBaseEntityWithMembers } from './entity-with-members.model'; import { IOrganizationCreateInput } from './organization.model'; import { IUser, LanguagesEnum } from './user.model'; import { ITag } from './tag.model'; -import { IContact } from './contact.model'; -import { IBasePerTenantAndOrganizationEntityModel } from './base-entity.model'; +import { IContact, IContactCreateInput } from './contact.model'; +import { IBasePerTenantAndOrganizationEntityModel, IBasePerTenantAndOrganizationEntityMutationInput } from './base-entity.model'; import { ITimeLog } from './timesheet.model'; import { IRelationalImageAsset } from './image-asset.model'; @@ -49,9 +49,8 @@ export interface IOrganizationContactFindInput createdBy?: string; } -export interface IOrganizationContactCreateInput extends IContact, IBasePerTenantAndOrganizationEntityModel, IRelationalImageAsset { +export interface IOrganizationContactCreateInput extends IContactCreateInput, IBasePerTenantAndOrganizationEntityMutationInput, IRelationalImageAsset { name: string; - contactId?: string; primaryEmail?: string; primaryPhone?: string; phones?: string[]; @@ -61,6 +60,8 @@ export interface IOrganizationContactCreateInput extends IContact, IBasePerTenan imageUrl?: string; contactType?: ContactType; createdBy?: string; + contact?: IContact; + contactId?: IContact['id']; } export interface IOrganizationContactUpdateInput extends IOrganizationContactCreateInput { @@ -80,8 +81,7 @@ export interface IOrganizationContactRegistrationInput { contactOrganization: IOrganizationCreateInput; } -export interface IOrganizationContactAcceptInviteInput - extends IOrganizationContactRegistrationInput { +export interface IOrganizationContactAcceptInviteInput extends IOrganizationContactRegistrationInput { inviteId: string; originalUrl?: string; } diff --git a/packages/core/src/core/utils.ts b/packages/core/src/core/utils.ts index 48dea9051bd..daaf0726f40 100644 --- a/packages/core/src/core/utils.ts +++ b/packages/core/src/core/utils.ts @@ -531,7 +531,7 @@ export function parseTypeORMFindToMikroOrm(options: FindManyOptions): { // Parses TypeORM `skip` option to MikroORM `offset` option if (options && options.skip) { - mikroOptions.offset = options.skip; + mikroOptions.offset = options.take * (options.skip - 1); } // Parses TypeORM `take` option to MikroORM `limit` option diff --git a/packages/core/src/employee/employee.entity.ts b/packages/core/src/employee/employee.entity.ts index 75120a370ae..4c015d72ecf 100644 --- a/packages/core/src/employee/employee.entity.ts +++ b/packages/core/src/employee/employee.entity.ts @@ -341,11 +341,11 @@ export class Employee extends TenantOrganizationBaseEntity implements IEmployee */ @ApiProperty({ type: () => User }) @MultiORMOneToOne(() => User, (it) => it.employee, { + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, - /** Database cascade action on delete. */ onDelete: 'CASCADE', - + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() @@ -362,11 +362,11 @@ export class Employee extends TenantOrganizationBaseEntity implements IEmployee */ @ApiProperty({ type: () => Contact }) @MultiORMOneToOne(() => Contact, (contact) => contact.employee, { + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, - /** Database cascade action on delete. */ onDelete: 'SET NULL', - + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() diff --git a/packages/core/src/organization-contact/commands/handlers/organization-contact-create.handler.ts b/packages/core/src/organization-contact/commands/handlers/organization-contact-create.handler.ts index f45bf842776..ea82b967802 100644 --- a/packages/core/src/organization-contact/commands/handlers/organization-contact-create.handler.ts +++ b/packages/core/src/organization-contact/commands/handlers/organization-contact-create.handler.ts @@ -1,6 +1,6 @@ -import { IEmployee, IOrganizationContact, IOrganizationProject } from '@gauzy/contracts'; +import { IOrganizationContact, IOrganizationProject } from '@gauzy/contracts'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; -import { isEmpty } from '@gauzy/common'; +import { isEmpty, isNotEmpty } from '@gauzy/common'; import { In } from 'typeorm'; import { OrganizationContactCreateCommand } from '../organization-contact-create.command'; import { OrganizationContactService } from '../../organization-contact.service'; @@ -15,39 +15,46 @@ export class OrganizationContactCreateHandler implements ICommandHandler { + /** + * Executes the creation of an organization contact. + * + * @param command An instance of OrganizationContactCreateCommand containing the necessary input for creating a new organization contact. + * @returns A promise that resolves to the newly created organization contact (IOrganizationContact). + */ + public async execute(command: OrganizationContactCreateCommand): Promise { try { + // Destructure the input from the command. const { input } = command; - - const tenantId = RequestContext.currentTenantId(); + // Destructure organizationId from the input, and get tenantId either from the current RequestContext or from the input. const { organizationId } = input; + const tenantId = RequestContext.currentTenantId() || input.tenantId; + + // Check if the input members are empty and projects are defined. + if (isEmpty(input.members) && isNotEmpty(input.projects)) { + // Map the projects to their IDs. + const projectIds = input.projects.map((project) => project.id); + + // Retrieve projects with specified IDs, belonging to the given organization and tenant. + const projects = await this.organizationProjectService.find({ + where: { + id: In(projectIds), + organization: { id: organizationId }, + tenantId + }, + relations: { members: true } + }); - /** - * If members is not selected, but project already has members - */ - if (isEmpty(input.members)) { - if (input.projects) { - const projectIds = input.projects.map((project: IOrganizationProject) => project.id); - const projects = await this.organizationProjectService.find({ - where: { - id: In(projectIds), - organizationId, - tenantId - }, - relations: { - members: true - } - }); - const members: IEmployee[][] = projects.map((project: IOrganizationProject) => project.members); - input.members = [].concat(...members); - } + // Flatten the members from these projects and assign them to input.members. + input.members = projects.flatMap((project: IOrganizationProject) => project.members); } - return await this.organizationContactService.create(input); + // Create a new organization contact with the modified input. + return await this.organizationContactService.create({ + ...input, + organization: { id: organizationId } + }); } catch (error) { - console.log('Error while creating new organization contact', error); + console.error('Error while creating new organization contact', error); } } } diff --git a/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts b/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts index ab146cfec9d..98b715a35f6 100644 --- a/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts +++ b/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts @@ -11,19 +11,27 @@ export class OrganizationContactUpdateHandler implements ICommandHandler { + /** + * Updates an organization contact based on a given command and retrieves the updated contact. + * + * @param command Contains the ID and new data for updating the organization contact. + * @returns A Promise resolving to the updated organization contact. + * @throws BadRequestException for any errors during the update process. + */ + public async execute(command: OrganizationContactUpdateCommand): Promise { try { const { id, input } = command; - //We are using create here because create calls the method save() - //We need save() to save ManyToMany relations + + // Update the organization contact using the provided ID and input data. await this._organizationContactService.create({ ...input, id }); - return await this._organizationContactService.findOneByIdString(id); + + // Retrieve and return the updated organization contact. + return this._organizationContactService.findOneByIdString(id); } catch (error) { + // Re-throw the error as a BadRequestException. throw new BadRequestException(error); } } From b2b3c49139cac43fe3f75c420909ae68f47ace2a Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:24:25 +0530 Subject: [PATCH 3/8] fix: save organization contact --- .../app/pages/contacts/contacts.component.ts | 72 +++++++++++++------ .../src/organization-contact.model.ts | 4 +- .../handlers/organization.create.handler.ts | 4 +- packages/core/src/tags/tag.entity.ts | 3 + 4 files changed, 56 insertions(+), 27 deletions(-) diff --git a/apps/gauzy/src/app/pages/contacts/contacts.component.ts b/apps/gauzy/src/app/pages/contacts/contacts.component.ts index 62bdb1fde7d..bd743275427 100644 --- a/apps/gauzy/src/app/pages/contacts/contacts.component.ts +++ b/apps/gauzy/src/app/pages/contacts/contacts.component.ts @@ -10,7 +10,8 @@ import { IContact, ICountry, ContactType, - ContactOrganizationInviteStatus + ContactOrganizationInviteStatus, + IContactCreateInput } from '@gauzy/contracts'; import { NbDialogService } from '@nebular/theme'; import { TranslateService } from '@ngx-translate/core'; @@ -362,20 +363,19 @@ export class ContactsComponent extends PaginationFilterBaseComponent implements .subscribe(); } + /** + * + * @param organizationContact + * @returns + */ public async addOrEditOrganizationContact(organizationContact: IOrganizationContactCreateInput) { - const contact: IContact = { - country: organizationContact.country, - city: organizationContact.city, - address: organizationContact.address, - address2: organizationContact.address2, - postcode: organizationContact.postcode, - fax: organizationContact.fax, - fiscalInformation: organizationContact.fiscalInformation, - website: organizationContact.website, - latitude: organizationContact.latitude, - longitude: organizationContact.longitude - }; - const payload = { + if (!this.organization) { + return; + } + const { id: organizationId, tenantId } = this.organization; + const contact: IContactCreateInput = this.extractLocation(organizationContact, organizationId, tenantId); + + const request = { ...organizationContact, contact }; @@ -383,17 +383,15 @@ export class ContactsComponent extends PaginationFilterBaseComponent implements try { if (organizationContact.name) { const { name } = organizationContact; + if (organizationContact.id) { - await this.organizationContactService.update(organizationContact.id, payload); - this.toastrService.success('NOTES.ORGANIZATIONS.EDIT_ORGANIZATIONS_CONTACTS.UPDATE_CONTACT', { - name - }); + await this.organizationContactService.update(organizationContact.id, request); + this.toastrService.success('NOTES.ORGANIZATIONS.EDIT_ORGANIZATIONS_CONTACTS.UPDATE_CONTACT', { name }); } else { - await this.organizationContactService.create(payload); - this.toastrService.success('NOTES.ORGANIZATIONS.EDIT_ORGANIZATIONS_CONTACTS.ADD_CONTACT', { - name - }); + await this.organizationContactService.create(request); + this.toastrService.success('NOTES.ORGANIZATIONS.EDIT_ORGANIZATIONS_CONTACTS.ADD_CONTACT', { name }); } + this.showAddCard = !this.showAddCard; this._refresh$.next(true); this.contacts$.next(true); @@ -405,6 +403,36 @@ export class ContactsComponent extends PaginationFilterBaseComponent implements } } + /** + * + * @param organizationContact + * @param organizationId + * @param tenantId + * @returns + */ + private extractLocation( + organizationContact: IOrganizationContactCreateInput, + organizationId: string, + tenantId: string + ): IContactCreateInput { + return { + country: organizationContact.country, + city: organizationContact.city, + address: organizationContact.address, + address2: organizationContact.address2, + postcode: organizationContact.postcode, + fax: organizationContact.fax, + fiscalInformation: organizationContact.fiscalInformation, + website: organizationContact.website, + latitude: organizationContact.latitude, + longitude: organizationContact.longitude, + organizationId, + organization: { id: organizationId }, + tenantId, + tenant: { id: tenantId } + }; + } + /* * Register Smart Table Source Config */ diff --git a/packages/contracts/src/organization-contact.model.ts b/packages/contracts/src/organization-contact.model.ts index 2bbd10164fd..3b06b220d69 100644 --- a/packages/contracts/src/organization-contact.model.ts +++ b/packages/contracts/src/organization-contact.model.ts @@ -60,8 +60,8 @@ export interface IOrganizationContactCreateInput extends IContactCreateInput, IB imageUrl?: string; contactType?: ContactType; createdBy?: string; - contact?: IContact; - contactId?: IContact['id']; + contact?: IContactCreateInput; + contactId?: IContactCreateInput['id']; } export interface IOrganizationContactUpdateInput extends IOrganizationContactCreateInput { diff --git a/packages/core/src/organization/commands/handlers/organization.create.handler.ts b/packages/core/src/organization/commands/handlers/organization.create.handler.ts index ca23c4aa99a..5b5a9045f2d 100644 --- a/packages/core/src/organization/commands/handlers/organization.create.handler.ts +++ b/packages/core/src/organization/commands/handlers/organization.create.handler.ts @@ -122,9 +122,7 @@ export class OrganizationCreateHandler implements ICommandHandler OrganizationTeam, (it) => it.labels, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + /** Database cascade action on delete. */ onDelete: 'SET NULL', }) From c6ed59a8851af1ad154dad643d28432ea2de820f Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:36:14 +0530 Subject: [PATCH 4/8] fix: updated crud service create method for mikro orm --- packages/core/src/core/crud/crud.service.ts | 44 +++++++++++---------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/core/src/core/crud/crud.service.ts b/packages/core/src/core/crud/crud.service.ts index 5d0d5f324b9..8c7fe01cb1d 100644 --- a/packages/core/src/core/crud/crud.service.ts +++ b/packages/core/src/core/crud/crud.service.ts @@ -13,7 +13,8 @@ import { UpdateResult } from 'typeorm'; import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; -import { CreateOptions, EntityData, FilterQuery as MikroFilterQuery, RequiredEntityData, UpsertOptions, wrap } from '@mikro-orm/core'; +import { CreateOptions, FilterQuery as MikroFilterQuery, RequiredEntityData, wrap } from '@mikro-orm/core'; +import { AssignOptions } from '@mikro-orm/knex'; import { IPagination } from '@gauzy/contracts'; import { BaseEntity } from '../entities/internal'; import { multiORMCreateQueryBuilder } from '../../core/orm/query-builder/query-builder.factory'; @@ -440,44 +441,45 @@ export abstract class CrudService implements ICrudService< * @returns The created or updated entity. */ public async create( - entity: IPartialEntity, + partialEntity: IPartialEntity, createOptions: CreateOptions = { /** This option disables the strict typing which requires all mandatory properties to have value, it has no effect on runtime */ partial: true, /** Creates a managed entity instance instead, bypassing the constructor call */ managed: true }, - upsertOptions: UpsertOptions = { - onConflictFields: ['id'], // specify a manual set of fields pass to the on conflict clause - onConflictExcludeFields: ['createdAt'], + assignOptions: AssignOptions = { + updateNestedEntities: false, + onlyOwnProperties: true } ): Promise { try { switch (this.ormType) { case MultiORMEnum.MikroORM: try { - // Create a new entity using MikroORM - const newEntity = this.mikroRepository.create(entity as RequiredEntityData, createOptions); - - // If the entity doesn't have an ID, it's new and should be persisted - if (!entity['id']) { - // Persisting the entities - await this.mikroRepository.persistAndFlush(newEntity); // This will also persist the relations - return this.serialize(newEntity); + if (partialEntity['id']) { + // Try to load the existing entity + const entity = await this.mikroRepository.findOne(partialEntity['id']); + if (entity) { + // If the entity has an ID, perform an upsert operation + this.mikroRepository.assign(entity, partialEntity as any, assignOptions); + await this.mikroRepository.flush(); + + return this.serialize(entity); + } } + // If the entity doesn't have an ID, it's new and should be persisted + // Create a new entity using MikroORM + const newEntity = this.mikroRepository.create(partialEntity as RequiredEntityData, createOptions); - // If the entity has an ID, perform an upsert operation - // This block will only be reached if the entity is existing - const upsertedEntity = await this.mikroRepository.upsert( - entity as EntityData | T, - upsertOptions - ); - return this.serialize(upsertedEntity); + // Persist new entity and flush + await this.mikroRepository.persistAndFlush(newEntity); // This will also persist the relations + return this.serialize(newEntity); } catch (error) { console.error('Error during mikro orm create crud transaction:', error); } case MultiORMEnum.TypeORM: - const newEntity = this.repository.create(entity as DeepPartial); + const newEntity = this.repository.create(partialEntity as DeepPartial); return await this.repository.save(newEntity); default: throw new Error(`Not implemented for ${this.ormType}`); From 55f3387418f9856ca7d4ff1834d2358b7103990a Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:23:34 +0530 Subject: [PATCH 5/8] fix: only used mappedBy inverse side option --- .../candidate-feedbacks.entity.ts | 11 +++++-- .../candidate-source.entity.ts | 5 ++- .../core/src/candidate/candidate.entity.ts | 33 ++++++++++++++++--- packages/core/src/contact/contact.entity.ts | 15 +++++++-- .../entity/relations/one-to-one.decorator.ts | 15 +++------ packages/core/src/deal/deal.entity.ts | 23 ++++++------- packages/core/src/employee/employee.entity.ts | 17 +++++++--- packages/core/src/merchant/merchant.entity.ts | 5 +++ .../organization-contact.entity.ts | 3 ++ .../product-setting/product-setting.entity.ts | 5 ++- .../product-variant-price.entity.ts | 3 ++ .../product-variant/product-variant.entity.ts | 10 ++++-- packages/core/src/user/user.entity.ts | 10 ++++-- .../core/src/warehouse/warehouse.entity.ts | 11 +++++-- 14 files changed, 123 insertions(+), 43 deletions(-) diff --git a/packages/core/src/candidate-feedbacks/candidate-feedbacks.entity.ts b/packages/core/src/candidate-feedbacks/candidate-feedbacks.entity.ts index 6261079fff4..6fc4e3cf807 100644 --- a/packages/core/src/candidate-feedbacks/candidate-feedbacks.entity.ts +++ b/packages/core/src/candidate-feedbacks/candidate-feedbacks.entity.ts @@ -105,12 +105,17 @@ export class CandidateFeedback extends TenantOrganizationBaseEntity /** * Candidate Interviewers */ - @ApiProperty({ type: () => CandidateInterviewers }) - @MultiORMOneToOne(() => CandidateInterviewers, { owner: true }) + @MultiORMOneToOne(() => CandidateInterviewers, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ + owner: true + }) @JoinColumn() interviewer?: ICandidateInterviewers; - @ApiProperty({ type: () => String }) + @ApiPropertyOptional({ type: () => String }) @RelationId((it: CandidateFeedback) => it.interviewer) @MultiORMColumn({ nullable: true, relationId: true }) interviewerId?: ICandidateInterviewers['id']; diff --git a/packages/core/src/candidate-source/candidate-source.entity.ts b/packages/core/src/candidate-source/candidate-source.entity.ts index 2cf69e792e3..b41d5c9d7ab 100644 --- a/packages/core/src/candidate-source/candidate-source.entity.ts +++ b/packages/core/src/candidate-source/candidate-source.entity.ts @@ -18,6 +18,9 @@ export class CandidateSource extends TenantOrganizationBaseEntity |-------------------------------------------------------------------------- */ - @MultiORMOneToOne(() => Candidate, (candidate) => candidate.source) + @MultiORMOneToOne(() => Candidate, (candidate) => candidate.source, { + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false + }) candidate?: ICandidate; } diff --git a/packages/core/src/candidate/candidate.entity.ts b/packages/core/src/candidate/candidate.entity.ts index ca9521b164e..1e9acb2ca8f 100644 --- a/packages/core/src/candidate/candidate.entity.ts +++ b/packages/core/src/candidate/candidate.entity.ts @@ -115,20 +115,27 @@ export class Candidate extends TenantOrganizationBaseEntity implements ICandidat /** * Contact */ - @ApiProperty({ type: () => Contact }) @MultiORMOneToOne(() => Contact, (contact) => contact.candidate, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, + + /** Database cascade action on delete. */ onDelete: 'SET NULL', + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() contact?: IContact; - @ApiProperty({ type: () => String, readOnly: true }) + @ApiPropertyOptional({ type: () => String }) @RelationId((it: Candidate) => it.contact) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - readonly contactId?: string; + contactId?: string; /* |-------------------------------------------------------------------------- @@ -154,9 +161,16 @@ export class Candidate extends TenantOrganizationBaseEntity implements ICandidat @ApiProperty({ type: () => CandidateSource }) @MultiORMOneToOne(() => CandidateSource, (candidateSource) => candidateSource.candidate, { + /** Indicates if relation column value can be nullable or not. */ nullable: true, + + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, + + /** Database cascade action on delete. */ onDelete: 'CASCADE', + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() @@ -173,8 +187,13 @@ export class Candidate extends TenantOrganizationBaseEntity implements ICandidat */ @ApiProperty({ type: () => User }) @MultiORMOneToOne(() => User, (user) => user.candidate, { + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, + + /** Database cascade action on delete. */ onDelete: 'CASCADE', + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() @@ -190,7 +209,13 @@ export class Candidate extends TenantOrganizationBaseEntity implements ICandidat * Employee */ @ApiProperty({ type: () => Employee }) - @MultiORMOneToOne(() => Employee, (employee) => employee.candidate, { owner: true }) + @MultiORMOneToOne(() => Employee, (employee) => employee.candidate, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ + owner: true + }) @JoinColumn() employee?: IEmployee; diff --git a/packages/core/src/contact/contact.entity.ts b/packages/core/src/contact/contact.entity.ts index f596a1627d0..9f31b0bd95e 100644 --- a/packages/core/src/contact/contact.entity.ts +++ b/packages/core/src/contact/contact.entity.ts @@ -119,27 +119,36 @@ export class Contact extends TenantOrganizationBaseEntity implements IContact { /** * Employee */ - @ApiProperty({ type: () => Employee }) @MultiORMOneToOne(() => Employee, (employee) => employee.contact, { + /** Database cascade action on delete. */ onDelete: 'SET NULL', + + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false }) employee?: IEmployee; /** * Employee */ - @ApiProperty({ type: () => Candidate }) @MultiORMOneToOne(() => Candidate, (candidate) => candidate.contact, { + /** Database cascade action on delete. */ onDelete: 'SET NULL', + + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false }) candidate?: ICandidate; /** * Organization Contact */ - @ApiProperty({ type: () => OrganizationContact }) @MultiORMOneToOne(() => OrganizationContact, (organizationContact) => organizationContact.contact, { + /** Database cascade action on delete. */ onDelete: 'SET NULL', + + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false }) organizationContact?: IOrganizationContact; } diff --git a/packages/core/src/core/decorators/entity/relations/one-to-one.decorator.ts b/packages/core/src/core/decorators/entity/relations/one-to-one.decorator.ts index d85fd99d94a..98cb6f0aa34 100644 --- a/packages/core/src/core/decorators/entity/relations/one-to-one.decorator.ts +++ b/packages/core/src/core/decorators/entity/relations/one-to-one.decorator.ts @@ -114,9 +114,9 @@ export function mapOneToOneArgsForMikroORM({ typeFunctionOrTarget, inverse const mikroOrmOptions: Partial> = { ...omit(options, 'onDelete', 'onUpdate') as Partial>, entity: typeFunctionOrTarget as (string | ((e?: any) => EntityName)), - cascade: mikroORMCascade, - deleteRule: typeOrmOptions?.onDelete?.toLocaleLowerCase(), - updateRule: typeOrmOptions?.onUpdate?.toLocaleLowerCase(), + ...(mikroORMCascade.length ? { cascade: mikroORMCascade } : {}), + ...(typeOrmOptions?.onDelete ? { deleteRule: typeOrmOptions?.onDelete?.toLocaleLowerCase() } : {}), + ...(typeOrmOptions?.onUpdate ? { updateRule: typeOrmOptions?.onUpdate?.toLocaleLowerCase() } : {}), ...(typeOrmOptions?.nullable ? { nullable: typeOrmOptions?.nullable } : {}), ...(typeOrmOptions?.lazy ? { lazy: typeOrmOptions?.lazy } : {}), }; @@ -124,16 +124,11 @@ export function mapOneToOneArgsForMikroORM({ typeFunctionOrTarget, inverse // Set default joinColumn if not overwritten in options if (mikroOrmOptions.owner === true && !mikroOrmOptions.joinColumn && propertyKey) { mikroOrmOptions.joinColumn = `${propertyKey}Id`; - mikroOrmOptions.referenceColumnName = `id`; } // Map inverseSideOrOptions based on the DB_ORM environment variable - if (process.env.DB_ORM == MultiORMEnum.MikroORM) { - if (mikroOrmOptions.owner === true) { - mikroOrmOptions.inversedBy = inverseSideOrOptions; - } else { - mikroOrmOptions.mappedBy = inverseSideOrOptions; - } + if (process.env.DB_ORM === MultiORMEnum.MikroORM && !mikroOrmOptions.owner) { + mikroOrmOptions.mappedBy = inverseSideOrOptions; } return mikroOrmOptions as MikroORMRelationOptions; diff --git a/packages/core/src/deal/deal.entity.ts b/packages/core/src/deal/deal.entity.ts index daf88b4f660..5904e7df897 100644 --- a/packages/core/src/deal/deal.entity.ts +++ b/packages/core/src/deal/deal.entity.ts @@ -33,7 +33,7 @@ export class Deal extends TenantOrganizationBaseEntity implements IDeal { @IsNotEmpty() @IsString() @MultiORMColumn() - public title: string; + title: string; @ApiProperty({ type: () => Number }) @IsOptional() @@ -41,7 +41,7 @@ export class Deal extends TenantOrganizationBaseEntity implements IDeal { @Min(0) @Max(5) @MultiORMColumn() - public probability?: number; + probability?: number; /* |-------------------------------------------------------------------------- @@ -57,7 +57,7 @@ export class Deal extends TenantOrganizationBaseEntity implements IDeal { joinColumn: 'createdByUserId', }) @JoinColumn({ name: 'createdByUserId' }) - public createdBy: IUser; + createdBy: IUser; @ApiProperty({ type: () => String }) @RelationId((it: Deal) => it.createdBy) @@ -65,7 +65,7 @@ export class Deal extends TenantOrganizationBaseEntity implements IDeal { @IsNotEmpty() @ColumnIndex() @MultiORMColumn({ relationId: true }) - public createdByUserId: string; + createdByUserId: string; /** * PipelineStage @@ -73,7 +73,7 @@ export class Deal extends TenantOrganizationBaseEntity implements IDeal { @ApiProperty({ type: () => PipelineStage }) @MultiORMManyToOne(() => PipelineStage, { onDelete: 'CASCADE' }) @JoinColumn() - public stage: IPipelineStage; + stage: IPipelineStage; @ApiProperty({ type: () => String }) @RelationId((it: Deal) => it.stage) @@ -81,7 +81,7 @@ export class Deal extends TenantOrganizationBaseEntity implements IDeal { @IsString() @ColumnIndex() @MultiORMColumn({ relationId: true }) - public stageId: string; + stageId: string; /* |-------------------------------------------------------------------------- @@ -92,19 +92,20 @@ export class Deal extends TenantOrganizationBaseEntity implements IDeal { /** * OrganizationContact */ - @ApiProperty({ type: () => OrganizationContact }) @MultiORMOneToOne(() => OrganizationContact, { + /** Database cascade action on delete. */ onDelete: 'CASCADE', - owner: true, - joinColumn: 'clientId', + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ + owner: true }) @JoinColumn() - public client: IOrganizationContact; + client: IOrganizationContact; @ApiProperty({ type: () => String }) @RelationId((it: Deal) => it.client) @IsOptional() @IsString() @MultiORMColumn({ nullable: true, relationId: true }) - public clientId: string; + clientId: string; } diff --git a/packages/core/src/employee/employee.entity.ts b/packages/core/src/employee/employee.entity.ts index 4c015d72ecf..7f39d35cc8f 100644 --- a/packages/core/src/employee/employee.entity.ts +++ b/packages/core/src/employee/employee.entity.ts @@ -340,11 +340,13 @@ export class Employee extends TenantOrganizationBaseEntity implements IEmployee * User */ @ApiProperty({ type: () => User }) - @MultiORMOneToOne(() => User, (it) => it.employee, { + @MultiORMOneToOne(() => User, (user) => user.employee, { /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, + /** Database cascade action on delete. */ onDelete: 'CASCADE', + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @@ -360,19 +362,23 @@ export class Employee extends TenantOrganizationBaseEntity implements IEmployee /** * Contact */ - @ApiProperty({ type: () => Contact }) @MultiORMOneToOne(() => Contact, (contact) => contact.employee, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, + /** Database cascade action on delete. */ onDelete: 'SET NULL', + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() contact?: IContact; - @ApiProperty({ type: () => String, readOnly: true }) + @ApiProperty({ type: () => String }) @RelationId((it: Employee) => it.contact) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) @@ -382,7 +388,10 @@ export class Employee extends TenantOrganizationBaseEntity implements IEmployee * Candidate */ @ApiProperty({ type: () => Candidate }) - @MultiORMOneToOne(() => Candidate, (candidate) => candidate.employee) + @MultiORMOneToOne(() => Candidate, (candidate) => candidate.employee, { + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false + }) candidate?: ICandidate; /* |-------------------------------------------------------------------------- diff --git a/packages/core/src/merchant/merchant.entity.ts b/packages/core/src/merchant/merchant.entity.ts index cc52371a788..51cada5a154 100644 --- a/packages/core/src/merchant/merchant.entity.ts +++ b/packages/core/src/merchant/merchant.entity.ts @@ -64,8 +64,13 @@ export class Merchant extends TenantOrganizationBaseEntity implements IMerchant */ @ApiProperty({ type: () => Contact }) @MultiORMOneToOne(() => Contact, { + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, + + /** Database cascade action on delete. */ onDelete: 'CASCADE', + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() diff --git a/packages/core/src/organization-contact/organization-contact.entity.ts b/packages/core/src/organization-contact/organization-contact.entity.ts index 718563c21ef..74ef4edfde7 100644 --- a/packages/core/src/organization-contact/organization-contact.entity.ts +++ b/packages/core/src/organization-contact/organization-contact.entity.ts @@ -111,10 +111,13 @@ export class OrganizationContact extends TenantOrganizationBaseEntity implements @MultiORMOneToOne(() => Contact, (contact) => contact.organizationContact, { /** Indicates if relation column value can be nullable or not. */ nullable: true, + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, + /** Database cascade action on delete. */ onDelete: 'SET NULL', + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) diff --git a/packages/core/src/product-setting/product-setting.entity.ts b/packages/core/src/product-setting/product-setting.entity.ts index cce01ff6bc8..80b3e34a75d 100644 --- a/packages/core/src/product-setting/product-setting.entity.ts +++ b/packages/core/src/product-setting/product-setting.entity.ts @@ -53,8 +53,11 @@ export class ProductVariantSetting extends TenantOrganizationBaseEntity implemen * ProductVariant */ @MultiORMOneToOne(() => ProductVariant, (productVariant) => productVariant.setting, { + /** Database cascade action on delete. */ onDelete: 'CASCADE', - owner: true, + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ + owner: true }) @JoinColumn() productVariant: ProductVariant; diff --git a/packages/core/src/product-variant-price/product-variant-price.entity.ts b/packages/core/src/product-variant-price/product-variant-price.entity.ts index 11d9a369441..270eb74c351 100644 --- a/packages/core/src/product-variant-price/product-variant-price.entity.ts +++ b/packages/core/src/product-variant-price/product-variant-price.entity.ts @@ -42,7 +42,10 @@ export class ProductVariantPrice extends TenantOrganizationBaseEntity implements * ProductVariant */ @MultiORMOneToOne(() => ProductVariant, (productVariant) => productVariant.price, { + /** Database cascade action on delete. */ onDelete: 'CASCADE', + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() diff --git a/packages/core/src/product-variant/product-variant.entity.ts b/packages/core/src/product-variant/product-variant.entity.ts index 9a4b82c2c81..55a8be90e7b 100644 --- a/packages/core/src/product-variant/product-variant.entity.ts +++ b/packages/core/src/product-variant/product-variant.entity.ts @@ -67,12 +67,15 @@ export class ProductVariant extends TenantOrganizationBaseEntity implements IPro /** * ProductVariantPrice */ - @MultiORMOneToOne(() => ProductVariantPrice, (variantPrice) => variantPrice.productVariant, { + @MultiORMOneToOne(() => ProductVariantPrice, (productVariantPrice) => productVariantPrice.productVariant, { /** Eager relations are always loaded automatically when relation's owner entity is loaded using find* methods. */ eager: true, /** Database cascade action on delete. */ onDelete: 'CASCADE', + + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false }) @JoinColumn() price: IProductVariantPrice; @@ -80,12 +83,15 @@ export class ProductVariant extends TenantOrganizationBaseEntity implements IPro /** * ProductVariantSetting */ - @MultiORMOneToOne(() => ProductVariantSetting, (settings) => settings.productVariant, { + @MultiORMOneToOne(() => ProductVariantSetting, (productVariantSetting) => productVariantSetting.productVariant, { /** Eager relations are always loaded automatically when relation's owner entity is loaded using find* methods. */ eager: true, /** Database cascade action on delete. */ onDelete: 'CASCADE', + + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false }) @JoinColumn() setting: IProductVariantSetting; diff --git a/packages/core/src/user/user.entity.ts b/packages/core/src/user/user.entity.ts index 32fa6bd8436..c8ebf8b64ed 100644 --- a/packages/core/src/user/user.entity.ts +++ b/packages/core/src/user/user.entity.ts @@ -216,13 +216,19 @@ export class User extends TenantBaseEntity implements IUser { /** * Employee */ - @MultiORMOneToOne(() => Employee, (it: Employee) => it.user) + @MultiORMOneToOne(() => Employee, (employee: Employee) => employee.user, { + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false + }) employee?: IEmployee; /** * Candidate */ - @MultiORMOneToOne(() => Candidate, (candidate: Candidate) => candidate.user) + @MultiORMOneToOne(() => Candidate, (candidate: Candidate) => candidate.user, { + /** This column is a boolean flag indicating that this is the inverse side of the relationship, and it doesn't control the foreign key directly */ + owner: false + }) candidate?: ICandidate; /* diff --git a/packages/core/src/warehouse/warehouse.entity.ts b/packages/core/src/warehouse/warehouse.entity.ts index a0d65878cca..13f2b62b703 100644 --- a/packages/core/src/warehouse/warehouse.entity.ts +++ b/packages/core/src/warehouse/warehouse.entity.ts @@ -80,10 +80,17 @@ export class Warehouse extends TenantOrganizationBaseEntity implements IWarehous /** * Contact */ - @ApiProperty({ type: () => Contact }) @MultiORMOneToOne(() => Contact, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + + /** If set to true then it means that related object can be allowed to be inserted or updated in the database. */ cascade: true, + + /** Database cascade action on delete. */ onDelete: 'CASCADE', + + /** This column is a boolean flag indicating whether the current entity is the 'owning' side of a relationship. */ owner: true }) @JoinColumn() @@ -93,7 +100,7 @@ export class Warehouse extends TenantOrganizationBaseEntity implements IWarehous @RelationId((it: Warehouse) => it.contact) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - contactId?: string; + contactId?: IContact['id']; /* |-------------------------------------------------------------------------- From e716b9ac0270dfc2aeade08150708640acf7492e Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:47:18 +0530 Subject: [PATCH 6/8] fix: additional create contact and assign it to org contact --- .../organization-contact-create.handler.ts | 22 ++++++++++++++----- .../organization-contact-update.handler.ts | 18 ++++++++++++++- .../organization-contact.entity.ts | 2 ++ .../organization-contact.module.ts | 2 ++ .../organization-contact.service.ts | 2 -- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/packages/core/src/organization-contact/commands/handlers/organization-contact-create.handler.ts b/packages/core/src/organization-contact/commands/handlers/organization-contact-create.handler.ts index ea82b967802..638310502de 100644 --- a/packages/core/src/organization-contact/commands/handlers/organization-contact-create.handler.ts +++ b/packages/core/src/organization-contact/commands/handlers/organization-contact-create.handler.ts @@ -6,13 +6,15 @@ import { OrganizationContactCreateCommand } from '../organization-contact-create import { OrganizationContactService } from '../../organization-contact.service'; import { OrganizationProjectService } from './../../../organization-project/organization-project.service'; import { RequestContext } from './../../../core/context'; +import { ContactService } from 'contact/contact.service'; @CommandHandler(OrganizationContactCreateCommand) export class OrganizationContactCreateHandler implements ICommandHandler { constructor( - private readonly organizationContactService: OrganizationContactService, - private readonly organizationProjectService: OrganizationProjectService + private readonly _organizationContactService: OrganizationContactService, + private readonly _organizationProjectService: OrganizationProjectService, + private readonly _contactService: ContactService, ) { } /** @@ -26,7 +28,7 @@ export class OrganizationContactCreateHandler implements ICommandHandler project.id); // Retrieve projects with specified IDs, belonging to the given organization and tenant. - const projects = await this.organizationProjectService.find({ + const projects = await this._organizationProjectService.find({ where: { id: In(projectIds), organization: { id: organizationId }, @@ -48,8 +50,18 @@ export class OrganizationContactCreateHandler implements ICommandHandler project.members); } + // Create contact details of organization + try { + input.contact = await this._contactService.create({ + ...input.contact, + organization: { id: organizationId } + }); + } catch (error) { + console.log('Error occurred during creation of contact details or creating the organization contact:', error); + } + // Create a new organization contact with the modified input. - return await this.organizationContactService.create({ + return await this._organizationContactService.create({ ...input, organization: { id: organizationId } }); diff --git a/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts b/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts index 98b715a35f6..3a9c2b0ccbe 100644 --- a/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts +++ b/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts @@ -1,14 +1,17 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { BadRequestException } from '@nestjs/common'; import { IOrganizationContact } from '@gauzy/contracts'; +import { RequestContext } from '../../../core/context'; import { OrganizationContactUpdateCommand } from '../organization-contact-update.command'; import { OrganizationContactService } from '../../organization-contact.service'; +import { ContactService } from '../../../contact/contact.service'; @CommandHandler(OrganizationContactUpdateCommand) export class OrganizationContactUpdateHandler implements ICommandHandler { constructor( - private readonly _organizationContactService: OrganizationContactService + private readonly _organizationContactService: OrganizationContactService, + private readonly _contactService: ContactService, ) { } /** @@ -22,6 +25,19 @@ export class OrganizationContactUpdateHandler implements ICommandHandler ImageAsset, { /** Indicates if relation column value can be nullable or not. */ nullable: true, + /** Database cascade action on delete. */ onDelete: 'SET NULL', + /** Eager relations are always loaded automatically when relation's owner entity is loaded using find* methods. */ eager: true }) diff --git a/packages/core/src/organization-contact/organization-contact.module.ts b/packages/core/src/organization-contact/organization-contact.module.ts index e694050768b..4ea56b9c546 100644 --- a/packages/core/src/organization-contact/organization-contact.module.ts +++ b/packages/core/src/organization-contact/organization-contact.module.ts @@ -10,6 +10,7 @@ import { CommandHandlers } from './commands/handlers'; import { RolePermissionModule } from '../role-permission/role-permission.module'; import { OrganizationModule } from './../organization/organization.module'; import { OrganizationProjectModule } from './../organization-project/organization-project.module'; +import { ContactModule } from '../contact/contact.module'; import { TypeOrmOrganizationContactRepository } from './repository'; @Module({ @@ -25,6 +26,7 @@ import { TypeOrmOrganizationContactRepository } from './repository'; RolePermissionModule, OrganizationModule, OrganizationProjectModule, + ContactModule, CqrsModule ], controllers: [OrganizationContactController], diff --git a/packages/core/src/organization-contact/organization-contact.service.ts b/packages/core/src/organization-contact/organization-contact.service.ts index c6ba011e232..c35a7bb03a8 100644 --- a/packages/core/src/organization-contact/organization-contact.service.ts +++ b/packages/core/src/organization-contact/organization-contact.service.ts @@ -1,5 +1,4 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; import { Brackets, In, Raw, WhereExpressionBuilder } from 'typeorm'; import { IEmployee, IOrganizationContact, IOrganizationContactFindInput, IPagination } from '@gauzy/contracts'; import { isPostgres } from '@gauzy/config'; @@ -14,7 +13,6 @@ import { TypeOrmOrganizationContactRepository } from './repository/type-orm-orga @Injectable() export class OrganizationContactService extends TenantAwareCrudService { constructor( - @InjectRepository(OrganizationContact) typeOrmOrganizationContactRepository: TypeOrmOrganizationContactRepository, mikroOrmOrganizationContactRepository: MikroOrmOrganizationContactRepository From 373298c0eece6cdf317961f179256aff49efa7fc Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:05:28 +0530 Subject: [PATCH 7/8] fix: updated organization contact mutation form --- .../contact-mutation.component.ts | 252 +++++++++--------- .../app/pages/contacts/contacts.component.ts | 35 +-- 2 files changed, 139 insertions(+), 148 deletions(-) diff --git a/apps/gauzy/src/app/pages/contacts/contact-mutation/contact-mutation.component.ts b/apps/gauzy/src/app/pages/contacts/contact-mutation/contact-mutation.component.ts index 483847e8dd2..696a06e134b 100644 --- a/apps/gauzy/src/app/pages/contacts/contact-mutation/contact-mutation.component.ts +++ b/apps/gauzy/src/app/pages/contacts/contact-mutation/contact-mutation.component.ts @@ -40,9 +40,10 @@ export class ContactMutationComponent extends TranslationBaseComponent FormHelpers: typeof FormHelpers = FormHelpers; - /* - * Getter & Setter for organizationContact element - */ + /** + * Getter and Setter for organizationContact. + * When a new organizationContact is set, synchronizes organization contact members. + */ private _organizationContact: IOrganizationContact; get organizationContact(): IOrganizationContact { return this._organizationContact; @@ -79,14 +80,12 @@ export class ContactMutationComponent extends TranslationBaseComponent /* * Output event emitter for cancel process event */ - @Output() - canceled = new EventEmitter(); + @Output() canceled = new EventEmitter(); /* * Output event emitter for add/edit organization contact event */ - @Output() - addOrEditOrganizationContact = new EventEmitter(); + @Output() addOrEditOrganizationContact = new EventEmitter(); // leaflet map template @ViewChild('leafletTemplate', { static: false }) leafletTemplate: LeafletMapComponent; @@ -97,7 +96,6 @@ export class ContactMutationComponent extends TranslationBaseComponent // form stepper @ViewChild('stepper') stepper: NbStepperComponent; - members: string[] = []; selectedMembers: IEmployee[] = []; selectedEmployeeIds: string[]; @@ -107,7 +105,6 @@ export class ContactMutationComponent extends TranslationBaseComponent projects: IOrganizationProject[] = []; employees: IEmployee[] = []; organization: IOrganization; - organizationContactBudgetTypeEnum = OrganizationContactBudgetTypeEnum; /** @@ -189,14 +186,11 @@ export class ContactMutationComponent extends TranslationBaseComponent } /** - * Sync organization contact members + * Sync organization contact members. + * Updates `selectedEmployeeIds` based on the members of the organization contact. */ syncOrganizationContactMembers() { - if (this.organizationContact) { - this.selectedEmployeeIds = this.organizationContact.members.map( - (member: IEmployee) => member.id - ); - } + this.selectedEmployeeIds = this.organizationContact?.members?.map((member: IEmployee) => member.id) ?? []; } /** @@ -212,73 +206,59 @@ export class ContactMutationComponent extends TranslationBaseComponent ); } + /** + * Fetches all projects associated with the current organization and user tenant, and updates the 'projects' property. + */ private async _getProjects() { - const { tenantId } = this.store.user; - const { id: organizationId } = this.organization; - const { items } = await this.organizationProjectsService.getAll([], { - organizationId, - tenantId - }); - this.projects = items; + try { + const { tenantId } = this.store.user; + const { id: organizationId } = this.organization; + + const { items } = await this.organizationProjectsService.getAll([], { + organizationId, + tenantId + }); + + this.projects = items; + } catch (error) { + console.error('Error fetching projects:', error); + } } private _patchForm() { if (!this.organization) { return; } + const orgContact = this.organizationContact; + // this.contMainForm.patchValue({ - imageUrl: this.organizationContact - ? this.organizationContact.imageUrl - : null, - tags: this.organizationContact - ? (this.organizationContact.tags) - : [], - name: this.organizationContact - ? this.organizationContact.name - : '', - primaryEmail: this.organizationContact - ? this.organizationContact.primaryEmail - : '', - primaryPhone: this.organizationContact - ? this.organizationContact.primaryPhone - : '', - projects: this.organizationContact - ? this.organizationContact.projects || [] - : [], - contactType: this.organizationContact - ? this.organizationContact.contactType - : this.contactType, - fax: this.organizationContact - ? this.organizationContact.contact - ? this.organizationContact.contact.fax - : '' - : '', - website: this.organizationContact - ? this.organizationContact.contact - ? this.organizationContact.contact.website - : '' - : '', - fiscalInformation: this.organizationContact - ? this.organizationContact.contact - ? this.organizationContact.contact.fiscalInformation - : '' - : '' + imageUrl: orgContact?.imageUrl ?? null, + tags: orgContact?.tags ?? [], + name: orgContact?.name ?? null, + primaryEmail: orgContact?.primaryEmail ?? null, + primaryPhone: orgContact?.primaryPhone ?? null, + projects: orgContact?.projects ?? [], + contactType: orgContact?.contactType ?? this.contactType, + fax: orgContact?.contact?.fax ?? null, + website: orgContact?.contact?.website ?? null, + fiscalInformation: orgContact?.contact?.fiscalInformation ?? null }); this.contMainForm.updateValueAndValidity(); + // this.budgetForm.patchValue({ - budgetType: this.organizationContact - ? this.organizationContact.budgetType - : OrganizationContactBudgetTypeEnum.HOURS, - budget: this.organizationContact - ? this.organizationContact.budget - : null + budgetType: orgContact?.budgetType ?? OrganizationContactBudgetTypeEnum.HOURS, + budget: orgContact?.budget ?? null }); this.budgetForm.updateValueAndValidity(); this._setLocationForm(); } + /** + * + * @returns + */ private _setLocationForm() { if (!this.organizationContact) { return; @@ -306,24 +286,27 @@ export class ContactMutationComponent extends TranslationBaseComponent this.toastrService.danger(error); } - addNewProject = (name: string): Promise => { + /** + * Add a new project. + * + * @param name Name of the project + * @returns A Promise resolving to the newly created IOrganizationProject + */ + addNewProject = async (name: string): Promise => { try { const { tenantId } = this.store.user; const { id: organizationId } = this.organization; - return this.organizationProjectsService - .create({ - name, - organizationId, - tenantId, - members: [] - }) - .then((project) => { - this.toastrService.success('NOTES.ORGANIZATIONS.EDIT_ORGANIZATIONS_PROJECTS.ADD_PROJECT', { - name - }); - return project; - }); + const project = await this.organizationProjectsService.create({ + name, + organizationId, + tenantId, + members: [] + }); + + this.toastrService.success('NOTES.ORGANIZATIONS.EDIT_ORGANIZATIONS_PROJECTS.ADD_PROJECT', { name }); + + return project; } catch (error) { this.errorHandler.handleError(error); } @@ -341,79 +324,96 @@ export class ContactMutationComponent extends TranslationBaseComponent this.canceled.emit(); } + /** + * Submits and processes form data for organizational contacts. + * Validates main form, consolidates data from multiple forms, processes members and project info, + * and emits the combined data for further action. + */ async submitForm() { if (this.contMainForm.invalid) { return; } - const { fiscalInformation, website, contactType } = this.contMainForm.getRawValue(); - const { tenantId } = this.store.user; - const { id: organizationId } = this.organization; - - const { budget, budgetType } = this.budgetForm.getRawValue(); - const { name, primaryEmail, primaryPhone, fax, tags = [] } = this.contMainForm.getRawValue(); - - let { imageUrl } = this.contMainForm.getRawValue(); + const { id: organizationId, tenantId } = this.organization; + const data = this.contMainForm.value; + const budget = this.budgetForm.value; const location = this.locationFormDirective.getValue(); + const { fax, fiscalInformation, website } = this.contMainForm.getRawValue(); + const { coordinates } = location['loc']; delete location['loc']; const [latitude, longitude] = coordinates; + + // Combining form data with additional properties const contact = { - ...{ organizationId, tenantId }, + latitude, + longitude, + fiscalInformation, + website, + fax, ...location, - ...{ latitude, longitude } + organization: { id: organizationId }, + tenantId, + tenant: { id: tenantId }, + ...(this.organizationContact?.contact?.id ? { id: this.organizationContact?.contact?.id } : {}) }; - let members = (this.members || this.selectedEmployeeIds || []) - .map((id) => this.employees.find((e) => e.id === id)) - .filter((e) => !!e); + /** + * Constructs an array of member objects from a list of member or selected employee IDs. + * Each ID is mapped to a corresponding employee object, filtering out any non-existent (falsy) members. + */ + let memberIds = this.members || this.selectedEmployeeIds || []; + let members = memberIds.map((id) => this.employees.find((e) => e.id === id)).filter(Boolean); + if (!members.length) members = this.selectedMembers; - let { projects = [] } = this.contMainForm.getRawValue(); - projects.map((project: IOrganizationProject) => { - if ('members' in project) { + // + let projects = data.projects ?? []; + projects.forEach((project: IOrganizationProject) => { + if (Array.isArray(project.members)) { project.members.push(...members); + } else { + project.members = [...members]; } - return project; }); this.addOrEditOrganizationContact.emit({ - id: this.organizationContact - ? this.organizationContact.id - : undefined, - organizationId, - tenantId, - budgetType, - budget, - name, - primaryEmail, - primaryPhone, + ...budget, + ...data, projects, - contactType, - imageUrl, members, - tags, - fax, - fiscalInformation, - website, - ...contact + contact, + organizationId, + organization: { id: organizationId }, + tenantId, + tenant: { id: tenantId }, + ...(this.organizationContact?.id ? { id: this.organizationContact?.id } : {}) }); } + /** + * Updates the 'tags' field in 'contMainForm' with the selected tags and revalidates the form. + * @param tags An array of selected tag objects. + */ selectedTagsEvent(tags: ITag[]) { this.contMainForm.patchValue({ tags }); this.contMainForm.updateValueAndValidity(); } + /** + * Progresses the stepper and adds a map marker on the second step. + */ nextStep() { this.stepper.next(); + + // Assuming the second step is related to map operations. if (this.stepper.selectedIndex === 1) { - const { - loc: { coordinates } - } = this.locationFormDirective.getValue(); - const [lat, lng] = coordinates; + // Directly destructure 'coordinates' from the location form value. + const { loc: { coordinates: [lat, lng] } } = this.locationFormDirective.getValue(); + + // Delay marker addition to ensure the map is ready. Adjust delay as needed. setTimeout(() => { this.leafletTemplate.addMarker(new LatLng(lat, lng)); }, 200); @@ -423,12 +423,8 @@ export class ContactMutationComponent extends TranslationBaseComponent /* * Google Place and Leaflet Map Coordinates Changed Event Emitter */ - onCoordinatesChanges( - $event: google.maps.LatLng | google.maps.LatLngLiteral - ) { - const { - loc: { coordinates } - } = this.locationFormDirective.getValue(); + onCoordinatesChanges($event: google.maps.LatLng | google.maps.LatLngLiteral) { + const { loc: { coordinates } } = this.locationFormDirective.getValue(); const [lat, lng] = coordinates; if (this.leafletTemplate) { @@ -436,20 +432,24 @@ export class ContactMutationComponent extends TranslationBaseComponent } } - /* - * Leaflet Map Click Event Emitter + /** + * Handles click events on the Leaflet map. Updates the location form with the clicked coordinates + * and resets some location-related fields. It also triggers a recalculation of dependent form values. + * + * @param latlng The latitude and longitude object from the map click event. */ onMapClicked(latlng: LatLng) { - const { lat, lng }: LatLng = latlng; const location = this.locationFormDirective.getValue(); + this.locationFormDirective.setValue({ ...location, country: '', loc: { type: 'Point', - coordinates: [lat, lng] + coordinates: [latlng.lat, latlng.lng] } }); + this.locationFormDirective.onCoordinatesChanged(); } diff --git a/apps/gauzy/src/app/pages/contacts/contacts.component.ts b/apps/gauzy/src/app/pages/contacts/contacts.component.ts index bd743275427..3cddd10ac27 100644 --- a/apps/gauzy/src/app/pages/contacts/contacts.component.ts +++ b/apps/gauzy/src/app/pages/contacts/contacts.component.ts @@ -364,6 +364,7 @@ export class ContactsComponent extends PaginationFilterBaseComponent implements } /** + * Manages adding or updating an organization contact and displays relevant notifications. * * @param organizationContact * @returns @@ -372,9 +373,8 @@ export class ContactsComponent extends PaginationFilterBaseComponent implements if (!this.organization) { return; } - const { id: organizationId, tenantId } = this.organization; - const contact: IContactCreateInput = this.extractLocation(organizationContact, organizationId, tenantId); + const contact: IContactCreateInput = this.extractLocation(organizationContact.contact); const request = { ...organizationContact, contact @@ -404,35 +404,26 @@ export class ContactsComponent extends PaginationFilterBaseComponent implements } /** + * A contact object with organization and tenant details from the current organization context. * - * @param organizationContact - * @param organizationId - * @param tenantId - * @returns + * @param contact The contact object to be enriched. + * @returns An enriched contact object containing location details, organization, and tenant information. */ - private extractLocation( - organizationContact: IOrganizationContactCreateInput, - organizationId: string, - tenantId: string - ): IContactCreateInput { + private extractLocation(contact: IContactCreateInput): IContactCreateInput | undefined { + if (!this.organization) { + return; + } + const { id: organizationId, tenantId } = this.organization; return { - country: organizationContact.country, - city: organizationContact.city, - address: organizationContact.address, - address2: organizationContact.address2, - postcode: organizationContact.postcode, - fax: organizationContact.fax, - fiscalInformation: organizationContact.fiscalInformation, - website: organizationContact.website, - latitude: organizationContact.latitude, - longitude: organizationContact.longitude, + ...contact, organizationId, organization: { id: organizationId }, tenantId, - tenant: { id: tenantId } + tenant: { id: tenantId }, }; } + /* * Register Smart Table Source Config */ From 12324c316382a35dc8708a09b26ffcec4caa98c3 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:13:37 +0530 Subject: [PATCH 8/8] fix: deepscan --- .../handlers/organization-contact-update.handler.ts | 1 - .../organization-contact/organization-contact.entity.ts | 3 --- .../organization-contact/organization-contact.service.ts | 8 +++----- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts b/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts index 3a9c2b0ccbe..c70aac9b8a9 100644 --- a/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts +++ b/packages/core/src/organization-contact/commands/handlers/organization-contact-update.handler.ts @@ -1,7 +1,6 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { BadRequestException } from '@nestjs/common'; import { IOrganizationContact } from '@gauzy/contracts'; -import { RequestContext } from '../../../core/context'; import { OrganizationContactUpdateCommand } from '../organization-contact-update.command'; import { OrganizationContactService } from '../../organization-contact.service'; import { ContactService } from '../../../contact/contact.service'; diff --git a/packages/core/src/organization-contact/organization-contact.entity.ts b/packages/core/src/organization-contact/organization-contact.entity.ts index 142dcee6e61..9b08b7a1852 100644 --- a/packages/core/src/organization-contact/organization-contact.entity.ts +++ b/packages/core/src/organization-contact/organization-contact.entity.ts @@ -3,7 +3,6 @@ import { JoinTable, RelationId } from 'typeorm'; -import { EntityRepositoryType } from '@mikro-orm/core'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsOptional, IsUUID } from 'class-validator'; import { @@ -50,8 +49,6 @@ import { MikroOrmOrganizationContactRepository } from './repository/mikro-orm-or @MultiORMEntity('organization_contact', { mikroOrmRepository: () => MikroOrmOrganizationContactRepository }) export class OrganizationContact extends TenantOrganizationBaseEntity implements IOrganizationContact { - [EntityRepositoryType]?: MikroOrmOrganizationContactRepository; - @ApiProperty({ type: () => String }) @ColumnIndex() @MultiORMColumn() diff --git a/packages/core/src/organization-contact/organization-contact.service.ts b/packages/core/src/organization-contact/organization-contact.service.ts index c35a7bb03a8..dc9671deefc 100644 --- a/packages/core/src/organization-contact/organization-contact.service.ts +++ b/packages/core/src/organization-contact/organization-contact.service.ts @@ -7,15 +7,13 @@ import { RequestContext } from '../core/context'; import { PaginationParams, TenantAwareCrudService } from './../core/crud'; import { OrganizationContact } from './organization-contact.entity'; import { prepareSQLQuery as p } from './../database/database.helper'; -import { MikroOrmOrganizationContactRepository } from './repository/mikro-orm-organization-contact.repository'; -import { TypeOrmOrganizationContactRepository } from './repository/type-orm-organization-contact.repository'; +import { MikroOrmOrganizationContactRepository, TypeOrmOrganizationContactRepository } from './repository'; @Injectable() export class OrganizationContactService extends TenantAwareCrudService { constructor( - typeOrmOrganizationContactRepository: TypeOrmOrganizationContactRepository, - - mikroOrmOrganizationContactRepository: MikroOrmOrganizationContactRepository + readonly typeOrmOrganizationContactRepository: TypeOrmOrganizationContactRepository, + readonly mikroOrmOrganizationContactRepository: MikroOrmOrganizationContactRepository ) { super(typeOrmOrganizationContactRepository, mikroOrmOrganizationContactRepository); }