Skip to content

Commit 48959bb

Browse files
authored
FCPDAL-64 - Add business and customer search extra params (#231)
* Add business and customer search extra perams * Updates from code review * Update CUSTOMER_NAME doc to last name only
1 parent c0f127c commit 48959bb

25 files changed

Lines changed: 1305 additions & 7 deletions

File tree

app/data-sources/rural-payments/RuralPaymentsBusiness.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { BadRequest, NotFound } from '../../errors/graphql.js'
44
import { RURALPAYMENTS_API_NOT_FOUND_001 } from '../../logger/codes.js'
55
import { formatDateAsUtcDateTime } from '../../utils/date.js'
66
import { postPutHeaders } from '../../utils/headers.js'
7+
import { getSearchOffsetAndLimit } from '../../utils/pagination.js'
78
import { RuralPayments } from './RuralPayments.js'
89

910
export const formatDateDDMMMYY = (date) => {
@@ -61,6 +62,27 @@ export class RuralPaymentsBusiness extends RuralPayments {
6162
return organisationResponse._data[0]
6263
}
6364

65+
async organisationSearch(searchFieldType, primarySearchPhrase, pagination) {
66+
const { offset, limit } = getSearchOffsetAndLimit(pagination)
67+
68+
const body = JSON.stringify({
69+
searchFieldType,
70+
primarySearchPhrase,
71+
offset,
72+
limit
73+
})
74+
75+
const response = await this.post('organisation/search', {
76+
body,
77+
headers: postPutHeaders
78+
})
79+
80+
return {
81+
data: response?._data ?? [],
82+
page: response?._page
83+
}
84+
}
85+
6486
async getOrganisationIdBySBI(sbi) {
6587
if (this.gatewayType === 'external') {
6688
return this.extractOrgIdFromDefraIdToken(sbi)

app/data-sources/rural-payments/RuralPaymentsCustomer.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ import { config } from '../../config.js'
33
import { NotFound } from '../../errors/graphql.js'
44
import { RURALPAYMENTS_API_NOT_FOUND_001 } from '../../logger/codes.js'
55
import { postPutHeaders } from '../../utils/headers.js'
6+
import { getSearchOffsetAndLimit } from '../../utils/pagination.js'
67
import { RuralPayments } from './RuralPayments.js'
78

9+
// Maps DAL customer search field types to the values KITS expects.
10+
const KITS_CUSTOMER_SEARCH_FIELD = {
11+
CRN: 'CUSTOMER_REFERENCE'
12+
}
13+
814
export class RuralPaymentsCustomer extends RuralPayments {
915
async getPersonIdByCRN(crn) {
1016
if (this.gatewayType === 'external') {
@@ -38,6 +44,27 @@ export class RuralPaymentsCustomer extends RuralPayments {
3844
return customerResponse._data[0]
3945
}
4046

47+
async personSearch(searchType, primarySearchPhrase, pagination) {
48+
const { offset, limit } = getSearchOffsetAndLimit(pagination)
49+
50+
const body = JSON.stringify({
51+
searchFieldType: KITS_CUSTOMER_SEARCH_FIELD[searchType] ?? searchType,
52+
primarySearchPhrase,
53+
offset,
54+
limit
55+
})
56+
57+
const response = await this.post('person/search', {
58+
body,
59+
headers: postPutHeaders
60+
})
61+
62+
return {
63+
data: response?._data ?? [],
64+
page: response?._page
65+
}
66+
}
67+
4168
async getCustomerByCRN(crn) {
4269
const personId = await this.getPersonIdByCRN(crn)
4370
return this.getPersonByPersonId(personId)

app/graphql/resolvers/business/query.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { transformPageInfo } from '../../../transformers/common.js'
2+
import { transformOrganisationSearchResult } from '../../../transformers/rural-payments/business.js'
13
import { retrieveOrgIdBySbi } from './common.js'
24

35
export const Query = {
@@ -10,5 +12,18 @@ export const Query = {
1012
land: { sbi },
1113
payments: { sbi }
1214
}
15+
},
16+
17+
async businessSearch(__, { searchString, searchType, pagination }, { dataSources }) {
18+
const { data, page } = await dataSources.ruralPaymentsBusiness.organisationSearch(
19+
searchType,
20+
searchString,
21+
pagination
22+
)
23+
24+
return {
25+
results: data.map(transformOrganisationSearchResult),
26+
pageInfo: transformPageInfo(page)
27+
}
1328
}
1429
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
1+
import { transformPageInfo } from '../../../transformers/common.js'
2+
import { transformPersonSearchResult } from '../../../transformers/rural-payments/customer.js'
13
import { retrievePersonIdByCRN } from './common.js'
24

35
export const Query = {
46
async customer(__, { crn }, { dataSources }) {
57
const personId = await retrievePersonIdByCRN(crn, dataSources)
68

79
return { crn, personId }
10+
},
11+
12+
async customerSearch(__, { searchString, searchType, pagination }, { dataSources }) {
13+
const { data, page } = await dataSources.ruralPaymentsCustomer.personSearch(
14+
searchType,
15+
searchString,
16+
pagination
17+
)
18+
19+
return {
20+
results: data.map(transformPersonSearchResult),
21+
pageInfo: transformPageInfo(page)
22+
}
823
}
924
}

app/graphql/types/business/query.gql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,13 @@ extend type Query {
22
business(sbi: ID!): Business
33
@on
44
@auth(requires: [CONSOLIDATED_VIEW, SINGLE_FRONT_DOOR, SFI_REFORM])
5+
6+
"""
7+
Search for businesses by SBI, business name, or business postcode.
8+
"""
9+
businessSearch(
10+
searchString: String!
11+
searchType: BusinessSearchFieldType!
12+
pagination: Pagination
13+
): BusinessSearchResponse @on @auth(requires: [CONSOLIDATED_VIEW, SINGLE_FRONT_DOOR, SFI_REFORM])
514
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""
2+
The field to search businesses by.
3+
"""
4+
enum BusinessSearchFieldType {
5+
"""
6+
The full 9-digit Single Business Identifier (SBI); returns 0 or 1 results.
7+
"""
8+
SBI
9+
10+
"""
11+
A partial business name, at least 2 characters; returns 0 or more results.
12+
"""
13+
BUSINESS_NAME
14+
15+
"""
16+
A full business postcode, at least 5 characters; returns 0 or more results.
17+
"""
18+
BUSINESS_POSTCODE
19+
}
20+
21+
"""
22+
Summary details of a business returned by a business search.
23+
"""
24+
type BusinessSearchResult {
25+
"""
26+
Internal SitiAgri/Portal unique identifier for the business.
27+
"""
28+
organisationId: ID @on
29+
30+
"""
31+
Single Business Identifier (SBI) of the business.
32+
"""
33+
sbi: ID @on
34+
35+
"""
36+
Name of the business.
37+
"""
38+
name: String @on
39+
40+
"""
41+
Other SBIs by which the business is known.
42+
"""
43+
additionalSbis: [ID] @on
44+
45+
"""
46+
Address of the business.
47+
"""
48+
address: Address @on
49+
50+
"""
51+
Correspondence address of the business.
52+
"""
53+
correspondenceAddress: Address @on
54+
55+
"""
56+
States the business address is also the financial address.
57+
"""
58+
isFinancialToBusinessAddress: Boolean @on
59+
60+
"""
61+
States the correspondence address should be used as the business address.
62+
"""
63+
isCorrespondenceAsBusinessAddress: Boolean @on
64+
65+
"""
66+
Whether the land for the business has been confirmed.
67+
"""
68+
landConfirmed: Boolean @on
69+
70+
"""
71+
The date the business was last updated.
72+
"""
73+
lastUpdated: Date @on
74+
75+
"""
76+
The status of the business.
77+
"""
78+
status: EntityStatus @on
79+
}
80+
81+
"""
82+
A page of business search results.
83+
"""
84+
type BusinessSearchResponse {
85+
"""
86+
The businesses matching the search.
87+
"""
88+
results: [BusinessSearchResult] @on
89+
90+
"""
91+
Paging information for the search results.
92+
"""
93+
pageInfo: PageInfo @on
94+
}

app/graphql/types/common.gql

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,29 @@ type EntityStatus {
137137
deactivated: Boolean @on
138138
}
139139

140+
"Paging information for a page of search results"
141+
type PageInfo {
142+
"""
143+
Current page number
144+
"""
145+
number: Int @on
146+
147+
"""
148+
Number of items per page
149+
"""
150+
size: Int @on
151+
152+
"""
153+
Total number of pages
154+
"""
155+
totalPages: Int @on
156+
157+
"""
158+
Total number of items across all pages
159+
"""
160+
totalElements: Int @on
161+
}
162+
140163
"Represents data about a pagination details for a list of items"
141164
input Pagination {
142165
"""

app/graphql/types/customer/query.gql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,14 @@ extend type Query {
22
customer(crn: ID!): Customer
33
@on
44
@auth(requires: [CONSOLIDATED_VIEW, SINGLE_FRONT_DOOR, SFI_REFORM])
5+
6+
"""
7+
Search for customers by CRN, personal identifier, customer name, customer postcode,
8+
vendor number, or trader number.
9+
"""
10+
customerSearch(
11+
searchString: String!
12+
searchType: CustomerSearchFieldType!
13+
pagination: Pagination
14+
): CustomerSearchResponse @on @auth(requires: [CONSOLIDATED_VIEW, SINGLE_FRONT_DOOR, SFI_REFORM])
515
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""
2+
The field to search customers by.
3+
"""
4+
enum CustomerSearchFieldType {
5+
"""
6+
The full 10-digit Customer Reference Number (CRN); returns 0 or 1 results.
7+
"""
8+
CRN
9+
10+
"""
11+
The full personal identifier, at least 9 characters; returns 0 or 1 results.
12+
"""
13+
PERSONAL_IDENTIFIER
14+
15+
"""
16+
A partial last name, at least 3 characters; returns 0 or more results.
17+
"""
18+
CUSTOMER_NAME
19+
20+
"""
21+
A full customer postcode, at least 5 characters; returns 0 or more results.
22+
"""
23+
CUSTOMER_POSTCODE
24+
25+
"""
26+
At least the first 6 characters of the vendor number; returns 0 or more results.
27+
"""
28+
VENDOR_NUMBER
29+
30+
"""
31+
At least the first 6 characters of the trader number; returns 0 or more results.
32+
"""
33+
TRADER_NUMBER
34+
}
35+
36+
"""
37+
Summary details of a customer returned by a customer search.
38+
"""
39+
type CustomerSearchResult {
40+
"""
41+
Internal SitiAgri/Portal unique identifier of the customer.
42+
"""
43+
personId: ID @on
44+
45+
"""
46+
Customer Reference Number (CRN) of the customer.
47+
"""
48+
crn: ID @on
49+
50+
"""
51+
Full name of the customer.
52+
"""
53+
fullName: String @on
54+
55+
"""
56+
Primary address of the customer.
57+
"""
58+
address: Address @on
59+
60+
"""
61+
Other IDs for this customer.
62+
"""
63+
personalIdentifiers: [String] @on
64+
65+
"""
66+
National Insurance number of the customer.
67+
"""
68+
nationalInsuranceNumber: String @on
69+
70+
"""
71+
Email address of the customer.
72+
"""
73+
email: String @on
74+
75+
"""
76+
The status of the customer record/account.
77+
"""
78+
status: EntityStatus @on
79+
}
80+
81+
"""
82+
A page of customer search results.
83+
"""
84+
type CustomerSearchResponse {
85+
"""
86+
The customers matching the search.
87+
"""
88+
results: [CustomerSearchResult] @on
89+
90+
"""
91+
Paging information for the search results.
92+
"""
93+
pageInfo: PageInfo @on
94+
}

app/transformers/common.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ export const transformEntityStatus = (entity) => ({
5454
confirmed: booleanise(entity?.confirmed)
5555
})
5656

57+
export const transformPageInfo = (page) => ({
58+
number: page?.number,
59+
size: page?.size,
60+
totalPages: page?.totalPages,
61+
totalElements: page?.totalElements
62+
})
63+
5764
export function transformToISODate(timestamp) {
5865
if (!timestamp || typeof timestamp === 'boolean' || typeof timestamp === 'object') {
5966
return null

0 commit comments

Comments
 (0)