import * as PropertyController from '../../../controllers/property' import { prismaClient as prisma } from '../../../prismaClient' import GeneralUtils from '../../general' import * as UserController from '../../../controllers/user' import { condoManagerLogger } from '../../logger' type CmUser = { email: string mobilePhone: string homePhone: string workPhone: string } // Those unit numbers are used in Walter by property manager and our staff to test things const TEST_PROPERTIES = ['999', '9999', '99999'] export async function checkDuplicatesAccountStatements({ projectId }: { projectId: string }) { condoManagerLogger.info('Checking duplicated account statements') const properties = await prisma.property.findMany({ where: { building: { project: { id: projectId } } }, select: { id: true, accountStatements: { select: { id: true, receiptNumber: true, transactionNumber: true, }, }, }, }) let accountStatementsDeletedIds = [] properties.map(async (property) => { for await (const accountStatement of property.accountStatements) { const sameAccountStatements = property.accountStatements.filter( (existingAccountStatement) => { if ( existingAccountStatement.receiptNumber && accountStatement.receiptNumber && existingAccountStatement.transactionNumber && accountStatement.transactionNumber ) { return ( existingAccountStatement.transactionNumber === accountStatement.transactionNumber && existingAccountStatement.receiptNumber === accountStatement.receiptNumber ) } return false } ) if (sameAccountStatements.length > 1) { const accountStatementsToDeleteIds = sameAccountStatements .filter((ac) => !accountStatementsDeletedIds[ac.id]) .map((ac) => ac.id) if (accountStatementsToDeleteIds.length) { condoManagerLogger.info( `Deleting ${accountStatementsToDeleteIds.length} account statements because they already exists` ) await prisma.accountStatement .deleteMany({ where: { id: { in: accountStatementsToDeleteIds, }, }, }) .catch(console.error) accountStatementsDeletedIds = [ ...accountStatementsDeletedIds, ...accountStatementsToDeleteIds, ] } } } }) condoManagerLogger.info('Done checking duplicated account statements') } export async function checkDuplicatesUnits({ projectId }: { projectId: string }) { condoManagerLogger.info('Checking duplicated properties') const properties = await prisma.property.findMany({ where: { building: { project: { id: { equals: projectId } } } }, include: { address: true, users: true, owners: true, }, }) await Promise.all( properties.map(async (property) => { // TMP logics to delete any units that we created in double because of wrong logics before const existingUnitsForCompte = await prisma.property.findMany({ where: { AND: [ { building: { project: { id: { equals: projectId } } }, }, { address: { apartmentNumber: { equals: property.address.apartmentNumber } }, }, ], }, select: { id: true, address: { select: { apartmentNumber: true, }, }, users: { select: { id: true, }, }, owners: { select: { id: true, }, }, }, }) if (existingUnitsForCompte.length > 1) { condoManagerLogger.info(`More than 1 unit existing for ${property.address.apartmentNumber}`) const unitsMappedForTotalResidents = existingUnitsForCompte.map( (property) => property.users.length + property.owners.length ) const maxNumberOfResident = Math.max(...unitsMappedForTotalResidents) const unitWithMaxNumberOfResident = existingUnitsForCompte.find( (property) => property.users.length + property.owners.length === maxNumberOfResident ) const unitsToDelete = existingUnitsForCompte.filter( (unit) => unit.id !== unitWithMaxNumberOfResident.id ) await PropertyController.updateProperty({ where: { id: unitWithMaxNumberOfResident.id }, data: { users: { connect: unitsToDelete .map((property) => property.users) .flat() .map((user) => ({ id: user.id })), }, owners: { connect: unitsToDelete .map((property) => property.owners) .flat() .map((user) => ({ id: user.id })), }, }, }) condoManagerLogger.info('Merged wrong units residents to the good unit') await Promise.all( unitsToDelete.map((unit) => prisma.property.delete({ where: { id: unit.id } })) ) condoManagerLogger.info('Merged wrong units residents to the good unit') } }) ) condoManagerLogger.info('Done checking duplicated properties') } export const checkDuplicatesParkings = async ({ projectId }: { projectId: string }) => { try { condoManagerLogger.info('Checking if there\'s duplicated parkings to delete') await new Promise((resolve) => setTimeout(resolve, 2000)) // const parkings = await prisma.parking.findMany({ // where: { project: { id: { equals: projectId } } }, // }) const parkingsToDelete = [] //GeneralUtils.getDuplicates(parkings, ['number', 'cadastreNumber']) if (parkingsToDelete.length > 0) { // condoManagerLogger.error( // `🗑 Deleting duplicated parkings: ${parkingsToDelete.map( // ({ number, cadastreNumber }) => `(${number} - ${cadastreNumber})` // )}` // ) // await Promise.all( // parkingsToDelete.map((parkingToDelete) => // prisma.parking.delete({ where: { id: parkingToDelete.id } }) // ) // ) } condoManagerLogger.info('Done checking if there\'s duplicated parkings to delete') } catch (error) { condoManagerLogger.error(error) } } export async function checkDuplicatesLockers({ projectId }: { projectId: string }) { condoManagerLogger.info('Checking if there\'s duplicated lockers to delete') const lockers = await prisma.locker.findMany({ where: { project: { id: { equals: projectId } } }, }) const lockersToDelete = GeneralUtils.getDuplicates(lockers, ['number', 'cadastreNumber']) if (lockersToDelete.length > 0) { condoManagerLogger.info( `🗑 Deleting duplicated lockers: ${lockersToDelete.map( ({ number, cadastreNumber }) => `(${number} - ${cadastreNumber})` )}` ) await Promise.all( lockersToDelete.map((lockerToDelete) => prisma.locker.delete({ where: { id: lockerToDelete.id } }) ) ) } condoManagerLogger.info('Done checking if there\'s duplicated lockers to delete') } export async function validateLockersData({ projectId, condoManagerLockers, }: { projectId: string condoManagerLockers: any[] }) { condoManagerLogger.info('Checking if we created some lockers that doesn\'t exist in Condo Manager') const existingLockers = await prisma.locker.findMany({ where: { project: { id: { equals: projectId, }, }, }, }) for await (const locker of existingLockers) { // For now, since CM doesn't give us the cadastreNumber, we // don't want to delete for nothing... if (locker.cadastreNumber) { return } const condoManagerLocker = condoManagerLockers.some( (cmLocker) => cmLocker.number === locker.number ) if (!condoManagerLocker) { if (!TEST_PROPERTIES.includes(locker.number)) { condoManagerLogger.info( `Locker #${locker.number} - ${locker.id} is not in Condo Manager's data` ) await prisma.locker.delete({ where: { id: locker.id } }) } } } condoManagerLogger.info( 'Done checking if we created some lockers that doesn\'t exist in Condo Manager' ) } export async function validateParkingsData({ projectId, condoManagerParkings, }: { projectId: string condoManagerParkings: any[] }) { condoManagerLogger.info( 'Checking if we created some parkings that doesn\'t exist in Condo Manager' ) const existingParkings = await prisma.parking.findMany({ where: { project: { id: projectId, }, }, }) for await (const parking of existingParkings) { // For now, since CM doesn't give us the cadastreNumber, we // don't want to delete for nothing... if (parking.cadastreNumber) { return } const condoManagerParking = condoManagerParkings.some( (cmParking) => cmParking.number === parking.number ) if (!condoManagerParking) { if (!TEST_PROPERTIES.includes(parking.number)) { condoManagerLogger.info( `Parking #${parking.number} - ${parking.id} is not in Condo Manager's data` ) await prisma.parking.delete({ where: { id: parking.id } }) } } } condoManagerLogger.info( 'Done checking if we created some parkings that doesn\'t exist in Condo Manager' ) } function cmUserIsSameAsWalterUser(cmUser: CmUser, user: any) { if (cmUser.email) { return GeneralUtils.isSameEmail(user.email, cmUser.email) } if (cmUser.mobilePhone) { return GeneralUtils.isSamePhoneNumber(user.phone?.number, cmUser.mobilePhone) } if (cmUser.homePhone) { return GeneralUtils.isSamePhoneNumber(user.homePhone?.number, cmUser.homePhone) } if (cmUser.workPhone) { return GeneralUtils.isSamePhoneNumber(user.officePhone?.number, cmUser.workPhone) } return false } export const validateResidentAreInGoodProperties = async ({ projectId, condoManagerUnitWithResidents, }: { projectId: string condoManagerUnitWithResidents: any }) => { condoManagerLogger.info('Checking if residents are linked to the right unit') const existingWalterPropertiesForProject = await prisma.project .findOne({ where: { id: projectId } }) .building() .properties({ select: { id: true, }, }) for await (const property of existingWalterPropertiesForProject) { const foundProperty = await prisma.property.findOne({ where: { id: property.id }, select: { id: true, address: { select: { apartmentNumber: true, }, }, users: { select: { id: true, firstName: true, lastName: true, email: true, phone: { select: { number: true, }, }, homePhone: { select: { number: true, }, }, officePhone: { select: { number: true, }, }, condoManagerNomComptes: { select: { id: true, nomCompte: true, noCompteReel: true, userNumber: true, }, }, }, }, owners: { select: { id: true, firstName: true, lastName: true, email: true, phone: { select: { number: true, }, }, homePhone: { select: { number: true, }, }, officePhone: { select: { number: true, }, }, condoManagerNomComptes: { select: { id: true, nomCompte: true, noCompteReel: true, userNumber: true, }, }, }, }, }, }) // Find the CM unit owners const cmUnitWithOwners = condoManagerUnitWithResidents.find( ({ unitNumber }) => unitNumber === foundProperty.address.apartmentNumber ) // If we can't find it might be because it's a test unit... like 999 if (cmUnitWithOwners) { const walterResidentsOfProperty = GeneralUtils.uniq( [...foundProperty.users, ...foundProperty.owners], 'id' ) // For each residents in the property make sure we can have a link between CM and Walter's data await walterResidentsOfProperty.reduce(async (promise, walterResident) => { await promise const residentIsThere = cmUnitWithOwners.residents.some((cmResident) => cmUserIsSameAsWalterUser(cmResident, walterResident) ) // Might need to remove the user from the unit... maybe later because we need to be able to push into CM first if (!residentIsThere) { condoManagerLogger.info( `Can't find a link between the user ${ walterResident.email || GeneralUtils.getUserName(walterResident) } - ${walterResident.id} being in property`, cmUnitWithOwners ) } }, Promise.resolve) } } condoManagerLogger.info('Done checking if residents are linked to the right unit') }