diff --git a/core/src/adapters/leasing-adapter/listings.ts b/core/src/adapters/leasing-adapter/listings.ts index a422299fa..1a0b1812d 100644 --- a/core/src/adapters/leasing-adapter/listings.ts +++ b/core/src/adapters/leasing-adapter/listings.ts @@ -226,7 +226,6 @@ type GetListingsParams = { listingCategory?: 'PARKING_SPACE' | 'APARTMENT' | 'STORAGE' published?: boolean rentalRule?: 'SCORED' | 'NON_SCORED' - validToRentForContactCode?: string } const getListings = async ( @@ -238,11 +237,6 @@ const getListings = async ( if (params.published !== undefined) queryParams.append('published', params.published.toString()) if (params.rentalRule) queryParams.append('rentalRule', params.rentalRule) - if (params.validToRentForContactCode) - queryParams.append( - 'validToRentForContactCode', - params.validToRentForContactCode - ) try { const response = await axios.get( @@ -251,8 +245,12 @@ const getListings = async ( if (response.status !== 200) { logger.error( - { status: response.status, data: response.data }, - `Error getting listings from leasing, by: published ${params.published}, rentalRule ${params.rentalRule} and validToRentForContactCode ${params.validToRentForContactCode}` + { + status: response.status, + data: response.data, + query: queryParams.toString(), + }, + `Error getting listings from leasing` ) return { ok: false, err: 'unknown' } } @@ -260,8 +258,8 @@ const getListings = async ( return { ok: true, data: response.data.content } } catch (error) { logger.error( - error, - `Unknown error fetching listings by published ${params.published}, rentalRule ${params.rentalRule} and validToRentForContactCode ${params.validToRentForContactCode}` + { error: error, queryParams: queryParams.toString() }, + `Unknown error fetching listings by published` ) return { ok: false, err: 'unknown' } } diff --git a/core/src/services/lease-service/helpers/lease.ts b/core/src/services/lease-service/helpers/lease.ts new file mode 100644 index 000000000..5690212fa --- /dev/null +++ b/core/src/services/lease-service/helpers/lease.ts @@ -0,0 +1,20 @@ +import { Tenant } from '@onecore/types' + +export const isTenantAllowedToRentAParkingSpaceInThisResidentialArea = ( + residentialAreaCode: string, + tenant: Tenant +) => { + if ( + tenant.upcomingHousingContract && + tenant.upcomingHousingContract.residentialArea?.code === residentialAreaCode + ) { + return true + } + + if ( + tenant.currentHousingContract && + tenant.currentHousingContract.residentialArea?.code === residentialAreaCode + ) { + return true + } +} diff --git a/core/src/services/lease-service/listings.ts b/core/src/services/lease-service/listings.ts index 836445b1a..916dac60f 100644 --- a/core/src/services/lease-service/listings.ts +++ b/core/src/services/lease-service/listings.ts @@ -13,6 +13,7 @@ import { z } from 'zod' import * as leasingAdapter from '../../adapters/leasing-adapter' import * as internalParkingSpaceProcesses from '../../processes/parkingspaces/internal' import { ProcessStatus } from '../../common/types' +import { isTenantAllowedToRentAParkingSpaceInThisResidentialArea } from './helpers/lease' export const routes = (router: KoaRouter) => { /** @@ -75,11 +76,12 @@ export const routes = (router: KoaRouter) => { }) const query = querySchema.safeParse(ctx.query) + logger.debug({ query }, 'Parsed query parameters for GET /listings') + const result = await leasingAdapter.getListings({ listingCategory: query.data?.listingCategory, published: query.data?.published, rentalRule: query.data?.rentalRule, - validToRentForContactCode: query.data?.validToRentForContactCode, }) if (!result.ok) { @@ -114,8 +116,58 @@ export const routes = (router: KoaRouter) => { }) .filter((item): item is Listing => !!item) - ctx.status = 200 - ctx.body = { content: listingsWithRentalObjects, ...metadata } + logger.info( + { + numberOfListings: listingsWithRentalObjects.length, + }, + 'Listings Retrieved from Leasing GET /listings' + ) + + if (!query.data?.validToRentForContactCode) { + ctx.status = 200 + ctx.body = { content: listingsWithRentalObjects, ...metadata } + } else { + //filter listings on validToRentForContactCode + const tenantResult = await leasingAdapter.getTenantByContactCode( + query.data?.validToRentForContactCode + ) + let isTenant = true + + if (!tenantResult.ok) { + if (tenantResult.err === 'contact-not-tenant') { + isTenant = false + } else { + ctx.status = 500 + ctx.body = { error: 'Tenant could not be retrieved', ...metadata } + return + } + } + + var listings = listingsWithRentalObjects.filter((listing) => { + return ( + listing.rentalRule == 'NON_SCORED' || //all NON_SCORED will be included + (listing.rentalRule == 'SCORED' && + isTenant && + tenantResult.ok && + listing.rentalObject.residentialAreaCode && + isTenantAllowedToRentAParkingSpaceInThisResidentialArea( + listing.rentalObject.residentialAreaCode, + tenantResult.data + )) // all SCORED where tenant is allowed to rent will be included + ) + }) + + logger.debug( + { + numberOfListings: listings.length, + contactCode: query.data?.validToRentForContactCode, + }, + 'Listings filtered on contact GET /listings' + ) + + ctx.status = 200 + ctx.body = { content: listings, ...metadata } + } } catch (error) { logger.error(error, 'Error fetching listings with rental objects') ctx.status = 500 diff --git a/core/src/services/lease-service/tests/listings.test.ts b/core/src/services/lease-service/tests/listings.test.ts index 2bb7252b7..a713791ce 100644 --- a/core/src/services/lease-service/tests/listings.test.ts +++ b/core/src/services/lease-service/tests/listings.test.ts @@ -103,6 +103,7 @@ describe('GET /listings', () => { }) const parkingSpace = factory.vacantParkingSpace.build({ rentalObjectCode: '12345', + residentialAreaCode: 'AREA123', }) const getListingsSpy = jest @@ -113,17 +114,83 @@ describe('GET /listings', () => { .spyOn(tenantLeaseAdapter, 'getParkingSpaces') .mockResolvedValueOnce({ ok: true, data: [parkingSpace] }) + jest + .spyOn(tenantLeaseAdapter, 'getTenantByContactCode') + .mockResolvedValueOnce({ + ok: true, + data: factory.tenant.build({ + currentHousingContract: { residentialArea: { code: 'AREA123' } }, + }), + }) + const res = await request(app.callback()).get( '/listings?validToRentForContactCode=abc123' ) expect(getListingsSpy).toHaveBeenCalledWith({ - validToRentForContactCode: 'abc123', + listingCategory: undefined, + published: undefined, + rentalRule: undefined, + }) + expect(res.status).toBe(200) + expect(res.body).toEqual({ + content: [expect.objectContaining({ id: 1337 })], + }) + }) + + it('responds with a filtered list with filter on validToRentForContactCode', async () => { + const listings = [ + factory.listing.build({ + id: 1337, + rentalObjectCode: '12345', + }), + factory.listing.build({ + id: 1339, + rentalObjectCode: '32345', + }), + ] + const parkingSpaces = [ + factory.vacantParkingSpace.build({ + rentalObjectCode: '12345', + residentialAreaCode: 'AREA123', + }), + factory.vacantParkingSpace.build({ + rentalObjectCode: '32345', + residentialAreaCode: 'ANOTHER_AREA', + }), + ] + + const getListingsSpy = jest + .spyOn(tenantLeaseAdapter, 'getListings') + .mockResolvedValueOnce({ ok: true, data: listings }) + + jest + .spyOn(tenantLeaseAdapter, 'getParkingSpaces') + .mockResolvedValueOnce({ ok: true, data: parkingSpaces }) + + jest + .spyOn(tenantLeaseAdapter, 'getTenantByContactCode') + .mockResolvedValueOnce({ + ok: true, + data: factory.tenant.build({ + currentHousingContract: { residentialArea: { code: 'AREA123' } }, + }), + }) + + const res = await request(app.callback()).get( + '/listings?validToRentForContactCode=abc123' + ) + + expect(getListingsSpy).toHaveBeenCalledWith({ + listingCategory: undefined, + published: undefined, + rentalRule: undefined, }) expect(res.status).toBe(200) expect(res.body).toEqual({ content: [expect.objectContaining({ id: 1337 })], }) + expect(res.body.content).toHaveLength(1) }) it('responds with 500 on error', async () => {