Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2372, 23508 - redirect to direct pay, API and UI updates #282

Merged
merged 4 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion search-api/src/search_api/models/document_access_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ def json(self):
'submissionDate': self.submission_date.isoformat(),
'expiryDate': self.expiry_date.isoformat() if self.expiry_date else None,
'outputFileKey': self._output_file_key,
'submitter': self.submitter.display_name if self.submitter else None
'submitter': self.submitter.display_name if self.submitter else None,
'paymentToken': self.payment_token
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you just add a comment here that this is the 'invoice id' from the pay db

}

documents = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,22 @@ def create_invoice(document_access_request: DocumentAccessRequest, user_jwt: Jwt
header, business_json)

if payment_response.status_code in (HTTPStatus.OK, HTTPStatus.CREATED):
payment_completion_date = datetime.utcnow()
is_pad = payment_response.json().get('paymentMethod') == 'PAD'
today_utc = datetime.utcnow()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

datetime.now(tz=timezone.utc)
datetime.utcnow() = depreciated

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, pycharm shows the same !
I wasn't sure about updating it in just one place, but you are right. When changing the code, update it in place I changed if it does not break everything else :D

if is_pad:
document_access_request.status = DocumentAccessRequest.Status.PAID
document_access_request.payment_completion_date = today_utc
else:
document_access_request.status = DocumentAccessRequest.Status.CREATED

pid = payment_response.json().get('id')
document_access_request.payment_token = pid
document_access_request.payment_status_code = payment_response.json().get('statusCode', '')
document_access_request.payment_completion_date = payment_completion_date
validity_in_days = current_app.config.get('DOCUMENT_REQUEST_VALIDITY_DURATION', 14)
document_access_request.expiry_date = payment_completion_date + relativedelta(days=validity_in_days)
document_access_request.status = DocumentAccessRequest.Status.PAID
document_access_request.expiry_date = today_utc + relativedelta(days=validity_in_days)
document_access_request.save()
return {'isPaymentActionRequired': payment_response.json().get('isPaymentActionRequired',
False)}, HTTPStatus.CREATED

if payment_response.status_code == HTTPStatus.BAD_REQUEST:
# Set payment error type used to retrieve error messages from pay-api
error_type = payment_response.json().get('type')
Expand Down
4 changes: 0 additions & 4 deletions search-api/src/search_api/services/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ def validate_document_access_request(document_access_request_json: dict, account
if not account_org:
validation_errors.append({'error': 'Invalid Account'})

if account_org.get('orgType') != 'PREMIUM':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woohoo!

validation_errors.append({
'error': 'Document Access Request can be created only by a premium account user'})

documents = document_access_request_json.get('documentAccessRequest', {}).get('documents', [])
if not documents:
validation_errors.append({'error': 'Document list must contain atleast one document type'})
Expand Down
11 changes: 5 additions & 6 deletions search-api/tests/unit/api/businesses/test_document_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@
from search_api.models import Document, DocumentAccessRequest, User
from search_api.services import queue
from search_api.services.authz import STAFF_ROLE
from search_api.services.validator import RequestValidator
from search_api.services.flags import Flags

from tests.unit import MockResponse
from tests.unit.services.utils import create_header, helper_create_jwt
from tests.unit.services.utils import create_header


DOCUMENT_ACCESS_REQUEST_TEMPLATE = {
Expand Down Expand Up @@ -233,7 +232,7 @@ def create_user():
def _create_user(**kwargs):
if not kwargs:
return User()

return User(**kwargs)
return _create_user

Expand All @@ -256,7 +255,7 @@ def test_post_business_document_submit_ce_to_queue(ld, session, client, jwt, moc
idp_userid = '123'
iss = 'iss'
login_source = 'API_GW'

mocker.patch('search_api.services.validator.RequestValidator.validate_document_access_request',
return_value=[])
mocker.patch('search_api.resources.v1.businesses.documents.document_request.get_role',
Expand Down Expand Up @@ -287,7 +286,7 @@ def test_post_business_document_submit_ce_to_queue(ld, session, client, jwt, moc
HTTPStatus.OK)
mocker.patch('search_api.resources.v1.businesses.documents.document_request.get_business',
return_value=business_mock_response)

mock_pub = mocker.patch.object(queue, 'publish', return_value=[])

# set the test data for the flag
Expand All @@ -298,7 +297,7 @@ def test_post_business_document_submit_ce_to_queue(ld, session, client, jwt, moc
.variations(False, True)
.variation_for_user(flag_user['key'], flag_value)
.fallthrough_variation(False))

# Test
api_response = client.post(f'/api/v1/businesses/{business_identifier}/documents/requests',
data=json.dumps(DOCUMENT_ACCESS_REQUEST_TEMPLATE),
Expand Down
57 changes: 57 additions & 0 deletions search-api/tests/unit/mocks/create-invoice-cc-response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"_links": {
"collection": "/api/v1/payment-requests?invoice_id=41861",
"self": "/api/v1/payment-requests/41861"
},
"businessIdentifier": "FM1000026",
"corpTypeCode": "BUS",
"createdBy": "BCSC/ZGCBNHM6U7FPRXI7AJT6KFF7GSKY43TA",
"createdName": "BCREG2 Ayisha FIFTY",
"createdOn": "2024-10-25T21:19:17+00:00",
"details": [
{
"label": "Registration Number: ",
"value": "FM1000026"
}
],
"id": 41861,
"isPaymentActionRequired": true,
"lineItems": [
{
"description": "Business Summary",
"filingFees": 7.0,
"futureEffectiveFees": 0.0,
"gst": 0.0,
"id": 44464,
"priorityFees": 0.0,
"pst": 0.0,
"quantity": 1,
"serviceFees": 1.5,
"statusCode": "ACTIVE",
"total": 8.5,
"waivedBy": null,
"waivedFees": 0.0
}
],
"overdueDate": "2024-12-15T08:00:00+00:00",
"paid": 0.0,
"paymentAccount": {
"accountId": "3137",
"accountName": "Kial Dev 3 (BTR test account) - Public search access",
"billable": true,
"branchName": "Public search access"
},
"paymentMethod": "DIRECT_PAY",
"references": [
{
"createdOn": "2024-10-25T21:19:17+00:00",
"id": 34456,
"invoiceNumber": "REGUT00041861",
"statusCode": "ACTIVE"
}
],
"refund": 0.0,
"serviceFees": 1.5,
"statusCode": "CREATED",
"total": 8.5
}
49 changes: 49 additions & 0 deletions search-api/tests/unit/mocks/create-invoice-pad-response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"_links": {
"collection": "/api/v1/payment-requests?invoice_id=41863",
"self": "/api/v1/payment-requests/41863"
},
"businessIdentifier": "FM1000026",
"corpTypeCode": "BUS",
"createdBy": "BCSC/ZGCBNHM6U7FPRXI7AJT6KFF7GSKY43TA",
"createdName": "BCREG2 Ayisha FIFTY",
"createdOn": "2024-10-25T21:22:37+00:00",
"details": [
{
"label": "Registration Number: ",
"value": "FM1000026"
}
],
"id": 41863,
"isPaymentActionRequired": false,
"lineItems": [
{
"description": "Business Summary",
"filingFees": 7.0,
"futureEffectiveFees": 0.0,
"gst": 0.0,
"id": 44466,
"priorityFees": 0.0,
"pst": 0.0,
"quantity": 1,
"serviceFees": 1.5,
"statusCode": "ACTIVE",
"total": 7.0,
"waivedBy": null,
"waivedFees": 0.0
}
],
"overdueDate": "2024-12-15T08:00:00+00:00",
"paid": 0.0,
"paymentAccount": {
"accountId": "3101",
"accountName": "Kial Dev 2 (BTR test account)-Director search access",
"billable": true,
"branchName": "Director search access"
},
"paymentMethod": "PAD",
"refund": 0.0,
"serviceFees": 1.5,
"statusCode": "APPROVED",
"total": 8.5
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def test_document_access_request_json(session):
'id': document_access_request.id,
'outputFileKey': None,
'paymentStatus': 'COMPLETED',
'paymentToken': document_access_request.payment_token,
'status': 'PAID',
'submissionDate': document_access_request.submission_date.isoformat(),
'submitter': 'firstname lastname'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
from datetime import datetime
from http import HTTPStatus

import pytest
from flask import current_app, g

from search_api.models import DocumentAccessRequest, User
from search_api.request_handlers.document_access_request_handler import create_invoice, save_request


DOCUMENT_ACCESS_REQUEST_TEMPLATE = {
"documentAccessRequest":{
"documentAccessRequest": {
"documents": [
{
"type": "BUSINESS_SUMMARY_FILING_HISTORY"
Expand All @@ -33,9 +33,10 @@
}
}


def test_save_request(client, session, jwt, mocker):
"""Assert that request can be saved."""
g.jwt_oidc_token_info={}
g.jwt_oidc_token_info = {}
user = User(username='username', firstname='firstname', lastname='lastname', sub='sub', iss='iss', idp_userid='123')
user.save()
mocker.patch('search_api.models.User.get_or_create_user_by_jwt', return_value=user)
Expand All @@ -45,7 +46,10 @@ def test_save_request(client, session, jwt, mocker):
assert document_access_request.submitter.firstname == user.firstname


def test_create_invoice(client, session, jwt, mocker):
@pytest.mark.parametrize('test_name,mock_response,is_payment_completion_date_expected', [
('test_pad_invoice', {'id': 123, 'paymentMethod': 'PAD'}, True),
('test_pad_invoice', {'id': 123, 'paymentMethod': 'DIRECT_PAY'}, False)])
def test_create_invoice(client, session, jwt, mocker, test_name, mock_response, is_payment_completion_date_expected):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

"""Assert that access request is updated with payment details."""
document_access_request = DocumentAccessRequest(
business_identifier='CP1234567',
Expand All @@ -64,18 +68,22 @@ def test_create_invoice(client, session, jwt, mocker):
}
}

mock_response = MockResponse({'id': 123},HTTPStatus.CREATED)
mock_payment_response = MockResponse(mock_response, HTTPStatus.CREATED)
mocker.patch('search_api.request_handlers.document_access_request_handler.create_payment',
return_value=mock_response)
return_value=mock_payment_response)
mocker.patch('search_api.request_handlers.document_access_request_handler.get_role',
return_value='basic')

business_json = {'identifier': 'BC1234567', 'legalType': 'BC', 'legalName': 'Test - 1234567'}
create_invoice(document_access_request, jwt, request_json, business_json)

document_access_request = DocumentAccessRequest.find_by_id(document_access_request.id)

assert document_access_request.payment_token
assert document_access_request.payment_completion_date
if is_payment_completion_date_expected:
assert document_access_request.payment_completion_date
else:
assert not document_access_request.payment_completion_date
assert document_access_request.expiry_date


Expand All @@ -95,7 +103,7 @@ def test_create_invoice_failure(client, session, jwt, mocker):
'documentAccessRequest': document_access_request
}

mock_response = MockResponse({'type': 'BAD_REQUEST'},HTTPStatus.BAD_REQUEST)
mock_response = MockResponse({'type': 'BAD_REQUEST'}, HTTPStatus.BAD_REQUEST)
mocker.patch('search_api.request_handlers.document_access_request_handler.create_payment',
return_value=mock_response)
mocker.patch('search_api.request_handlers.document_access_request_handler.get_role',
Expand Down
17 changes: 0 additions & 17 deletions search-api/tests/unit/services/test_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,23 +71,6 @@ def test_document_access_request_valid(client, session, jwt, requests_mock):
assert err is None


def test_document_access_request_invalid_basic_account(client, session, jwt, requests_mock):
"""Assert that a auth-api user orgs request works as expected with the mock service endpoint."""
# setup
current_app.config.update(AUTH_SVC_URL=MOCK_URL_NO_KEY)
token = helper_create_jwt(jwt, [authz.PPR_ROLE])
USERS_ORG_COPY = copy.deepcopy(USERS_ORG)
USERS_ORG_COPY['orgs'][0]['orgType'] = 'BASIC'
org = USERS_ORG_COPY['orgs'][0]
requests_mock.get(f"{current_app.config.get('AUTH_SVC_URL')}users/orgs", json=USERS_ORG_COPY)
requests_mock.get(f"{current_app.config.get('AUTH_SVC_URL')}orgs/{org['id']}", json=org)

err =RequestValidator.validate_document_access_request(DOCUMENT_ACCESS_REQUEST_TEMPLATE, org['id'], token, 'basic')
# check

assert err[0]['error'] == 'Document Access Request can be created only by a premium account user'


@pytest.mark.parametrize('test_name, error_message', [
('no_documents', 'Document list must contain atleast one document type'),
('invalid_document_type', 'Invalid Document Type')
Expand Down
30 changes: 20 additions & 10 deletions search-ui/src/composables/document-access-request-factory.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { reactive } from 'vue'

import { DocumentType } from '@/enums'
import { StaffPaymentIF } from '@/interfaces'
import { AccessRequestsHistoryI, DocumentAccessRequestsI, CreateDocumentResponseI, DocumentI } from '@/interfaces'
import {
AccessRequestsHistoryI,
CreateDocumentResponseI,
DocumentAccessRequestsI,
DocumentI,
StaffPaymentIF
} from '@/interfaces'
import { EntityI } from '@/interfaces/entity'
import { getActiveAccessRequests, createDocumentAccessRequest, getDocument, fetchFilingDocument } from '@/requests'
import { Document } from '@/types'
import { createDocumentAccessRequest, fetchFilingDocument, getActiveAccessRequests, getDocument } from '@/requests'
import { Document } from '@/types'
import { DocumentAccessRequestPaymentStatus } from '@/enums/document-access-request-payment-status'


const documentAccessRequest = reactive({
Expand All @@ -14,7 +20,8 @@ const documentAccessRequest = reactive({
_error: null,
_loading: false,
_saving: false,
_downloading: false
_downloading: false,
_needsPayment: false
}) as DocumentAccessRequestsI

export const useDocumentAccessRequest = () => {
Expand Down Expand Up @@ -47,24 +54,27 @@ export const useDocumentAccessRequest = () => {
documentAccessRequest._error = response.error
} else {
documentAccessRequest.currentRequest = response.createDocumentResponse
}
if(documentAccessRequest.currentRequest.paymentStatus === DocumentAccessRequestPaymentStatus.CREATED) {
documentAccessRequest._needsPayment = true
}
}
documentAccessRequest._saving = false
}

const downloadDocument = async (businessIdentifier: string, document: DocumentI) => {
documentAccessRequest._downloading = true
documentAccessRequest._error = null
const response = await getDocument(businessIdentifier, document)
const response = await getDocument(businessIdentifier, document)
if (response?.error){
documentAccessRequest._error = response.error
}
}
documentAccessRequest._downloading = false
}

const downloadFilingDocument = async (businessIdentifier: string, filingId: number, document: Document) => {
const downloadFilingDocument = async (businessIdentifier: string, filingId: number, document: Document) => {
documentAccessRequest._downloading = true
documentAccessRequest._error = null
const response = await fetchFilingDocument(businessIdentifier, filingId, document)
const response = await fetchFilingDocument(businessIdentifier, filingId, document)
if (response?.error){
documentAccessRequest._error = response.error
}
Expand Down
4 changes: 4 additions & 0 deletions search-ui/src/enums/document-access-request-payment-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum DocumentAccessRequestPaymentStatus {
CREATED = 'CREATED',
PAID = 'PAID'
}
Loading
Loading