diff --git a/__tests__/core/findUserAddress.spec.js b/__tests__/core/findUserAddress.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..92b62d356d38a32aeb8982ce64b091d7ba1d1424 --- /dev/null +++ b/__tests__/core/findUserAddress.spec.js @@ -0,0 +1,50 @@ +const xml2js = require('xml2js') +const { errors } = require('cozy-konnector-libs') +const { findUserAddress } = require('../../src/core/findUserAddress') + +const mockSoapRequest = jest.fn() +jest.mock('easy-soap-request', () => async () => mockSoapRequest()) + +jest.spyOn(xml2js, 'parseStringPromise').mockResolvedValue({ + Envelope: { + Body: { + Fault: { detail: { erreur: { resultat: { $: { code: 401 } } } } }, + faultstring: 'Mock error', + }, + }, +}) + +const mockParseUserPdl = jest.fn() +jest.mock('../../src/helpers/parsing', () => ({ + parseUserPdl: () => mockParseUserPdl(), +})) + +const responseMock = { + response: { + body: 'mockedBody', + }, +} + +describe('findUserAddress', () => { + it('should throw LOGIN_FAIL if soapRequest fails', async () => { + mockSoapRequest.mockRejectedValueOnce('reject') + try { + await findUserAddress() + expect(true).toBe(false) + } catch (error) { + expect(error).toBe(errors.VENDOR_DOWN) + } + }) + + it('should return a correct pdl number', async () => { + mockSoapRequest.mockResolvedValue(responseMock) + mockParseUserPdl.mockResolvedValue('12345') + + try { + await findUserAddress() + expect(true).toBe(false) + } catch (error) { + expect(error).toBe(errors.NOT_EXISTING_DIRECTORY) + } + }) +}) diff --git a/__tests__/core/findUserPdl.spec.js b/__tests__/core/findUserPdl.spec.js index 140201b691964a0dc8a1ae75f79b92589e5d9697..d79e167a8b2f7cf9823a5d9e63dc5159151a9c4f 100644 --- a/__tests__/core/findUserPdl.spec.js +++ b/__tests__/core/findUserPdl.spec.js @@ -5,20 +5,6 @@ const { findUserPdl } = require('../../src/core/findUserPdl') const mockSoapRequest = jest.fn() jest.mock('easy-soap-request', () => async () => mockSoapRequest()) -jest.spyOn(xml2js, 'parseStringPromise').mockResolvedValue({ - Envelope: { - Body: { - Fault: { detail: { erreur: { resultat: { $: { code: 401 } } } } }, - faultstring: 'Mock error', - }, - }, -}) - -const mockParseUserPdl = jest.fn() -jest.mock('../../src/helpers/parsing', () => ({ - parseUserPdl: () => mockParseUserPdl(), -})) - const responseMock = { response: { body: 'mockedBody', @@ -26,21 +12,64 @@ const responseMock = { } describe('recherchePoint', () => { - it('should throw LOGIN_FAILED for too many responses', async () => { - mockSoapRequest.mockResolvedValue(responseMock) - mockParseUserPdl.mockImplementationOnce(() => { - throw new Error('Error') + beforeEach(() => { + jest.clearAllMocks() + }) + it('should return a correct pdl number ✅', async () => { + jest.spyOn(xml2js, 'parseStringPromise').mockResolvedValueOnce({ + Envelope: { + Body: { + rechercherPointResponse: { + points: { point: { $: { id: '191919119' } } }, + }, + }, + }, }) + mockSoapRequest.mockResolvedValueOnce(responseMock) - try { - await findUserPdl() - expect(true).toBe(false) - } catch (error) { - expect(error).toBe(errors.LOGIN_FAILED) - } + expect(await findUserPdl()).toBe('191919119') + }) + + it('should handle issue on parsing ✅', async () => { + const cozyKonnectorsLib = require('cozy-konnector-libs') + const spyer = jest.spyOn(cozyKonnectorsLib, 'log') + jest.spyOn(xml2js, 'parseStringPromise').mockResolvedValueOnce({ + Envelope: { + Body: { + Fault: { + detail: { + erreur: { + resultat: { + $: { + code: 401, + }, + }, + }, + }, + }, + }, + }, + }) + mockSoapRequest.mockResolvedValueOnce(responseMock) + + await findUserPdl() + // Only verifing that we are going through logs + expect(spyer).toBeCalledTimes(5) }) - it('should throw LOGIN_FAIL if soapRequest fails', async () => { + it('should throw LOGIN_FAIL if soapRequest fails 🚫', async () => { + const mockParseUserPdl = jest.fn() + jest.mock('../../src/helpers/parsing', () => ({ + parseUserPdl: () => mockParseUserPdl(), + })) + jest.spyOn(xml2js, 'parseStringPromise').mockResolvedValueOnce({ + Envelope: { + Body: { + Fault: { detail: { erreur: { resultat: { $: { code: 401 } } } } }, + faultstring: 'Mock error', + }, + }, + }) mockSoapRequest.mockRejectedValueOnce('reject') try { await findUserPdl() @@ -49,11 +78,4 @@ describe('recherchePoint', () => { expect(error).toBe(errors.LOGIN_FAILED) } }) - - it('should return a correct pdl number', async () => { - mockSoapRequest.mockResolvedValue(responseMock) - mockParseUserPdl.mockResolvedValue('12345') - - expect(await findUserPdl()).toBe('12345') - }) }) diff --git a/__tests__/core/verifyUserIdentity.spec.js b/__tests__/core/verifyUserIdentity.spec.js index 4c3dd1382efce5fbcae6c57e3ba7249696ef9410..390396dd5918cf588485c0ffd3a13e3656a0f699 100644 --- a/__tests__/core/verifyUserIdentity.spec.js +++ b/__tests__/core/verifyUserIdentity.spec.js @@ -1,20 +1,22 @@ const { errors } = require('cozy-konnector-libs') const { verifyUserIdentity } = require('../../src/core/verifyUserIdentity') +const { findUserPdl } = require('../../src/core/findUserPdl') +const { findUserAddress } = require('../../src/core/findUserAddress') + +jest.mock('../../src/core/findUserPdl') +jest.mock('../../src/core/findUserAddress') jest.mock('../../src/requests/insee', () => ({ getInseeCode: jest.fn().mockResolvedValue(69), })) -jest.mock('../../src/core/findUserPdl', () => ({ - findUserPdl: jest.fn().mockResolvedValue('12345'), -})) - jest.mock('../../src/index', () => ({ start: jest.fn(), })) describe('verifyUserIdentity', () => { it('should throw LOGIN_FAILED when pdl given and recieved are NOT matching 🚫', async () => { + findUserPdl.mockResolvedValueOnce('12345') try { await verifyUserIdentity( { @@ -32,7 +34,9 @@ describe('verifyUserIdentity', () => { expect(error).toBe(errors.LOGIN_FAILED) } }) + it('should throw TERMS_VERSION_MISMATCH when pdl give and recieved are NOT matching on alternate start 🚫', async () => { + findUserPdl.mockResolvedValueOnce('12345') try { await verifyUserIdentity( { @@ -53,6 +57,7 @@ describe('verifyUserIdentity', () => { }) it('should return void when pdl give and recieved are matching ✅', async () => { + findUserPdl.mockResolvedValueOnce('12345') expect.assertions(1) try { await verifyUserIdentity( @@ -72,6 +77,7 @@ describe('verifyUserIdentity', () => { } }) it('should return void when pdl give and recieved are matching with stored inseecode ✅', async () => { + findUserPdl.mockResolvedValue('12345') expect.assertions(1) try { await verifyUserIdentity( @@ -92,4 +98,62 @@ describe('verifyUserIdentity', () => { expect(true).toBe(false) } }) + + it('should return void when pdl give and recieved are matching with SGE second chance onboarding ✅ ', async () => { + findUserPdl.mockResolvedValueOnce(null).mockResolvedValueOnce('12345') + findUserAddress.mockResolvedValueOnce({ + escalierEtEtageEtAppartement: '12', + codePostal: '69003', + numeroEtNomVoie: '20 rue du lac', + commune: { $: { code: '69383' } }, + }) + + try { + await verifyUserIdentity( + { + lastname: 'John', + firstname: 'DOE', + address: '1 street', + pointId: '12345', + postalCode: '69069', + }, + 'azertyuiop', + 'apiKey', + 'login@user.com' + ) + expect(true).toBeTruthy() + } catch (error) { + expect(true).toBe(false) + } + }) + + it('should return void when pdl give and recieved are matching with SGE last chance onboarding✅ ', async () => { + findUserPdl + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(null) + .mockResolvedValueOnce('12345') + findUserAddress.mockResolvedValueOnce({ + escalierEtEtageEtAppartement: '12', + codePostal: '69003', + numeroEtNomVoie: '20 rue du lac', + commune: { $: { code: '69383' } }, + }) + + try { + await verifyUserIdentity( + { + name: 'John', + address: '1 street', + pointId: '12345', + postalCode: '69069', + }, + 'azertyuiop', + 'apiKey', + 'login@user.com' + ) + expect(true).toBeTruthy() + } catch (error) { + expect(true).toBe(false) + } + }) }) diff --git a/__tests__/helpers/parsing.spec.js b/__tests__/helpers/parsing.spec.js index 37fefc0fb31fc6b01f85d632690beba73fe40d7c..37577a3626fcb3db3fd7152d1107147bb4c9aef5 100644 --- a/__tests__/helpers/parsing.spec.js +++ b/__tests__/helpers/parsing.spec.js @@ -4,9 +4,13 @@ const { parseContracts, parseServiceId, parseSgeXmlData, + checkContractExists, + parseUserAddress, formateDataForDoctype, parseTags, parseValue, + removeMultipleSpaces, + removeAddressnumber, } = require('../../src/helpers/parsing') describe('parsing', () => { it('should parse userPdl', () => { @@ -132,6 +136,44 @@ describe('parsing', () => { ]) }) + it('should format existing contract', async () => { + const result = { + Envelope: { + Body: { + rechercherServicesSouscritsMesuresResponse: { + servicesSouscritsMesures: { + v: 14361, + d: '2021-08-01T00:00:00.000+02:00', + }, + }, + }, + }, + } + const reply = checkContractExists(result) + expect(reply).toEqual({ + v: 14361, + d: '2021-08-01T00:00:00.000+02:00', + }) + }) + + it('should format user address', async () => { + const result = { + Envelope: { + Body: { + consulterDonneesTechniquesContractuellesResponse: { + point: { + donneesGenerales: { adresseInstallation: { numero: '12' } }, + }, + }, + }, + }, + } + const reply = parseUserAddress(result) + expect(reply).toEqual({ + numero: '12', + }) + }) + it('should parseTag with :', () => { const reply = parseTags('test:tag') expect(reply).toBe('tag') @@ -149,4 +191,35 @@ describe('parsing', () => { const reply = parseValue(14361, 'w') expect(reply).toBe(14361) }) + it('should remove spaces ✅', () => { + const reply = removeMultipleSpaces('20 rue du lac') + expect(reply).toBe('20 rue du lac') + }) + + describe('removeAddressnumber', () => { + it('should remove address number ✅', () => { + const reply = removeAddressnumber('20 rue du lac') + expect(reply).toBe('rue du lac') + }) + it('should remove B and number ✅', () => { + const reply = removeAddressnumber('20 B rue du lac') + expect(reply).toBe('rue du lac') + }) + it('should remove b and number ✅', () => { + const reply = removeAddressnumber('20 B rue du lac') + expect(reply).toBe('rue du lac') + }) + it('should remove T and number ✅', () => { + const reply = removeAddressnumber('20 T rue du lac') + expect(reply).toBe('rue du lac') + }) + it('should remove t and number ✅', () => { + const reply = removeAddressnumber('20 t rue du lac') + expect(reply).toBe('rue du lac') + }) + it('should remove t and number ✅', () => { + const reply = removeAddressnumber('20t rue du lac') + expect(reply).toBe('rue du lac') + }) + }) }) diff --git a/importedData.json b/importedData.json index 5b3d3e991426163b55da470e84c7a4e80fab9b56..a60a84081c8b9982500f212da31ec8da3e7916cf 100644 --- a/importedData.json +++ b/importedData.json @@ -3,4 +3,4 @@ "com.grandlyon.enedis.year": [], "com.grandlyon.enedis.month": [], "com.grandlyon.enedis.minute": [] -} \ No newline at end of file +} diff --git a/src/core/findUserAddress.js b/src/core/findUserAddress.js new file mode 100644 index 0000000000000000000000000000000000000000..a4124fdadb052ed340184ad0eeaec9fa7e987e88 --- /dev/null +++ b/src/core/findUserAddress.js @@ -0,0 +1,54 @@ +// @ts-check +const { log, errors } = require('cozy-konnector-libs') +const soapRequest = require('easy-soap-request') +const { + parseTags, + parseValue, + parseUserAddress, +} = require('../helpers/parsing') +const xml2js = require('xml2js') +const { consulterDonneesTechniquesContractuelles } = require('../requests/sge') + +/** + * Get user contract start date + * @param {string} url + * @param {string} apiAuthKey + * @param {string} userLogin + * @param {number} pointId + * @returns {Promise<Address>} + */ +async function findUserAddress(url, apiAuthKey, userLogin, pointId) { + log('info', 'Fetching user address') + const sgeHeaders = { + 'Content-Type': 'text/xml;charset=UTF-8', + apikey: apiAuthKey, + } + + const { response } = await soapRequest({ + url: `${url}/enedis_SGE_ConsultationDonneesTechniquesContractuelles/1.0`, + headers: sgeHeaders, + xml: consulterDonneesTechniquesContractuelles(pointId, userLogin, false), + }).catch(err => { + log('error', 'Error while fetching user : ' + err) + throw errors.VENDOR_DOWN + }) + + const result = await xml2js.parseStringPromise(response.body, { + tagNameProcessors: [parseTags], + valueProcessors: [parseValue], + explicitArray: false, + }) + + try { + return parseUserAddress(result) + } catch (error) { + log('error', 'Error while processing user address: ' + error) + log( + 'error', + `Enedis issue ${result.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${result.Envelope.Body.Fault.faultstring}` + ) + throw errors.NOT_EXISTING_DIRECTORY + } +} + +module.exports = { findUserAddress } diff --git a/src/core/findUserPdl.js b/src/core/findUserPdl.js index b522561d93b23ddc7617e492123002d70cb0a6d3..b9edb6230ce0027aa10aa2c1afd7ee696eeb0a33 100644 --- a/src/core/findUserPdl.js +++ b/src/core/findUserPdl.js @@ -22,7 +22,8 @@ async function findUserPdl( name, address, postalCode, - inseeCode + inseeCode, + escalierEtEtageEtAppartement = '' ) { log('info', 'Fetching user data') const sgeHeaders = { @@ -33,7 +34,14 @@ async function findUserPdl( const { response } = await soapRequest({ url: url, headers: sgeHeaders, - xml: rechercherPoint(appLogin, name, postalCode, inseeCode, address), + xml: rechercherPoint( + appLogin, + name, + postalCode, + inseeCode, + address, + escalierEtEtageEtAppartement + ), }).catch(err => { log('error', 'rechercherPointResponse') log('error', err) @@ -56,7 +64,7 @@ async function findUserPdl( `Enedis issue ${parsedReply.Envelope.Body.Fault.detail.erreur.resultat.$.code}: ${parsedReply.Envelope.Body.Fault.faultstring}` ) } - throw errors.LOGIN_FAILED + return null } } diff --git a/src/core/index.js b/src/core/index.js index e9bf3edd24146cc7d8806a11d55bc6ac0ce24dba..70e2d7ee4c550722f40a6442b3f2ecf942870cd1 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -4,6 +4,7 @@ const { terminateContract } = require('./contractTermination') const { verifyContract } = require('./contractVerification') const { findUserPdl } = require('./findUserPdl') const { verifyUserIdentity } = require('./verifyUserIdentity') +const { findUserAddress } = require('./findUserAddress') module.exports = { activateContract, @@ -12,4 +13,5 @@ module.exports = { verifyContract, findUserPdl, verifyUserIdentity, + findUserAddress, } diff --git a/src/core/types/types.js b/src/core/types/types.js index 48cd577f8bfba6e992eb99e64ff42d822dc12462..df5323ee754adf436d21664755895248d5e6eb99 100644 --- a/src/core/types/types.js +++ b/src/core/types/types.js @@ -53,6 +53,7 @@ * @property {string} address * @property {string} inseeCode * @property {string} city + * @property {boolean} hasBeenThroughtSafetyOnBoarding */ /** @@ -89,3 +90,19 @@ * @property {number} consentId * @property {string} inseeCode */ + +/** + * Address definition + * @typedef {object} Address + * @property {string} escalierEtEtageEtAppartement + * @property {string} numeroEtNomVoie + * @property {string} codePostal + * @property {Commune} commune + */ + +/** + * Commune definition + * @typedef {object} Commune + * @property {{code: string}} $ + * @property {string} libelle + */ diff --git a/src/core/verifyUserIdentity.js b/src/core/verifyUserIdentity.js index 47e4a8fcf11b480b86fd911da9622ad8b26aff0d..dcdfcd25634dd0482302102aa61ce2ef974ed8ad 100644 --- a/src/core/verifyUserIdentity.js +++ b/src/core/verifyUserIdentity.js @@ -2,6 +2,11 @@ const { log, errors } = require('cozy-konnector-libs') const { findUserPdl } = require('./findUserPdl') const { getInseeCode } = require('../requests/insee') +const { findUserAddress } = require('./findUserAddress') +const { + removeMultipleSpaces, + removeAddressnumber, +} = require('../helpers/parsing') /** * Verify user identity @@ -26,7 +31,11 @@ async function verifyUserIdentity( inseeCode = await getInseeCode(fields.postalCode, fields.city) } - const pdl = await findUserPdl( + // Store if user is going through safety sge onboarding + let userSafetyOnBoarding = false + + // First try with user adresse + let pdl = await findUserPdl( `${baseUrl}/enedis_SDE_recherche-point/1.0`, apiAuthKey, loginUtilisateur, @@ -36,8 +45,52 @@ async function verifyUserIdentity( inseeCode ) + if (!pdl) { + log('warn', 'Second chance for sge onboarding') + // Set safety onboarding in order to save it inside BO + userSafetyOnBoarding = true + // Backup verification + const userAddress = await findUserAddress( + baseUrl, + apiAuthKey, + loginUtilisateur, + fields.pointId + ) + + const escalierEtEtageEtAppartement = userAddress.escalierEtEtageEtAppartement + ? removeMultipleSpaces(userAddress.escalierEtEtageEtAppartement) + : '' + + pdl = await findUserPdl( + `${baseUrl}/enedis_SDE_recherche-point/1.0`, + apiAuthKey, + loginUtilisateur, + fields.lastname, + removeMultipleSpaces(userAddress.numeroEtNomVoie), + userAddress.codePostal, + userAddress.commune.$.code, + escalierEtEtageEtAppartement + ) + + // Third try, remove address number because it's buggy on SGE side + if (!pdl) { + log('warn', 'Last chance onboarding for sge') + pdl = await findUserPdl( + `${baseUrl}/enedis_SDE_recherche-point/1.0`, + apiAuthKey, + loginUtilisateur, + fields.lastname, + removeMultipleSpaces(removeAddressnumber(userAddress.numeroEtNomVoie)), + userAddress.codePostal, + userAddress.commune.$.code, + escalierEtEtageEtAppartement + ) + } + } + if (fields.pointId != pdl) { log('error', 'PointId does not match') + if (isAlternateStart) { throw errors.TERMS_VERSION_MISMATCH } else { @@ -52,6 +105,7 @@ async function verifyUserIdentity( inseeCode, postalCode: fields.postalCode, address: fields.address, + hasBeenThroughtSafetyOnBoarding: userSafetyOnBoarding, city: fields.city, } } diff --git a/src/helpers/parsing.js b/src/helpers/parsing.js index 1d10a743d4072eb4de884392df68f5fa876e6a70..5141eb91b15b806a4050aa99c527f3186fab27ae 100644 --- a/src/helpers/parsing.js +++ b/src/helpers/parsing.js @@ -29,6 +29,18 @@ function parseContractStartDate(result) { 'dateDerniereModificationFormuleTarifaireAcheminement' ] } +/** + * Return User address + * @param {string} result + * @returns {Address} + */ +function parseUserAddress(result) { + log('info', 'Parsing user Address') + const json = JSON.stringify(result) + return JSON.parse(json)['Envelope']['Body'][ + 'consulterDonneesTechniquesContractuellesResponse' + ]['point']['donneesGenerales']['adresseInstallation'] +} /** * Return User contract start date @@ -127,6 +139,24 @@ function parseValue(value, name) { return value } +/** + * Remove SGE useless multiple white spaces + * @param {string} str + * @returns {string} + */ +function removeMultipleSpaces(str) { + return str.replace(/ +/g, ' ') +} + +/** + * Remove SGE address number + * @param {string} str + * @returns {string} + */ +function removeAddressnumber(str) { + return str.replace(/\d+ |b |B |T |t |\d+/g, '') +} + module.exports = { parseSgeXmlData, formateDataForDoctype, @@ -136,5 +166,8 @@ module.exports = { parseContracts, parseContractStartDate, parseServiceId, + parseUserAddress, checkContractExists, + removeMultipleSpaces, + removeAddressnumber, } diff --git a/src/index.js b/src/index.js index 484bebd2640a528cede2c2d0b700b2a22721735c..ae1faa8c5a6eac88feef3334066214014061a658 100644 --- a/src/index.js +++ b/src/index.js @@ -120,7 +120,8 @@ async function start(fields, cozyParameters) { user.address, user.postalCode, user.inseeCode, - user.city + user.city, + user.hasBeenThroughtSafetyOnBoarding ) // handle user contract start date in order to preperly request data diff --git a/src/requests/bo.js b/src/requests/bo.js index 236b2c8151b0ac25675f25bd91842d1d426f0801..c4805cc9e441916046afb4a63e6462a3a6ae04aa 100644 --- a/src/requests/bo.js +++ b/src/requests/bo.js @@ -10,6 +10,7 @@ const { default: axios } = require('axios') * @param {string} postalCode * @param {string} inseeCode * @param {string} city + * @param {boolean} safetyOnBoarding * @returns {Promise<Consent>} */ async function createBoConsent( @@ -21,7 +22,8 @@ async function createBoConsent( address, postalCode, inseeCode, - city + city, + safetyOnBoarding ) { log('info', `Query createBoConsent`) const headers = { @@ -41,6 +43,7 @@ async function createBoConsent( postalCode, inseeCode, city, + safetyOnBoarding, }, headers ) diff --git a/src/requests/sge.js b/src/requests/sge.js index 22e2d09c4ebce95bc36cc1a561ce4c4ab8443061..62bc3d66d81f5de07698a653c1c89d40a4d007ac 100644 --- a/src/requests/sge.js +++ b/src/requests/sge.js @@ -102,7 +102,11 @@ function consultationMesuresDetailleesMaxPower( * @param {string} appLogin * @returns {string} */ -function consulterDonneesTechniquesContractuelles(pointId, appLogin) { +function consulterDonneesTechniquesContractuelles( + pointId, + appLogin, + consent = true +) { log('info', `Query consulterDonneesTechniquesContractuelles`) return `<?xml version='1.0' encoding='utf-8'?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" @@ -113,7 +117,7 @@ function consulterDonneesTechniquesContractuelles(pointId, appLogin) { <v2:consulterDonneesTechniquesContractuelles> <pointId>${pointId}</pointId> <loginUtilisateur>${appLogin}</loginUtilisateur> - <autorisationClient>true</autorisationClient> + <autorisationClient>${consent}</autorisationClient> </v2:consulterDonneesTechniquesContractuelles> </soapenv:Body> </soapenv:Envelope> @@ -125,14 +129,45 @@ function consulterDonneesTechniquesContractuelles(pointId, appLogin) { * @param {string} name * @param {string} postalCode * @param {string} inseeCode - * @param {string} [address] + * @param {string} address + * @param {string} [escalierEtEtageEtAppartement] * @returns {string} PDL */ -function rechercherPoint(appLogin, name, postalCode, inseeCode, address) { +function rechercherPoint( + appLogin, + name, + postalCode, + inseeCode, + address, + escalierEtEtageEtAppartement +) { log( 'info', `Query rechercherPoint - postal code : ${postalCode} / insee code: ${inseeCode}` ) + if (escalierEtEtageEtAppartement) { + return `<?xml version='1.0' encoding='utf-8'?> + <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" + xmlns:v2="http://www.enedis.fr/sge/b2b/services/rechercherpoint/v2.0" + xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0"> + <soapenv:Header/> + <soapenv:Body> + <v2:rechercherPoint> + <criteres> + <adresseInstallation> + <escalierEtEtageEtAppartement>${escalierEtEtageEtAppartement}</escalierEtEtageEtAppartement> + <numeroEtNomVoie>${address}</numeroEtNomVoie> + <codePostal>${postalCode}</codePostal> + <codeInseeCommune>${inseeCode}</codeInseeCommune> + </adresseInstallation> + <nomClientFinalOuDenominationSociale>${name}</nomClientFinalOuDenominationSociale> + <rechercheHorsPerimetre>true</rechercheHorsPerimetre> + </criteres> + <loginUtilisateur>${appLogin}</loginUtilisateur> + </v2:rechercherPoint> + </soapenv:Body> + </soapenv:Envelope>` + } return `<?xml version='1.0' encoding='utf-8'?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v2="http://www.enedis.fr/sge/b2b/services/rechercherpoint/v2.0"