Skip to content
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
1 change: 1 addition & 0 deletions auth-api/src/auth_api/models/dataclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class ProductReviewTask:

org_id: str
org_name: str
org_access_type: str
product_code: str
product_description: str
product_subscription_id: int
Expand Down
52 changes: 39 additions & 13 deletions auth-api/src/auth_api/services/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from datetime import datetime
from typing import Any

from auth_api.services.rest_service import RestService
from flask import current_app
from sqlalchemy import and_, case, func, literal, or_
from sqlalchemy.exc import SQLAlchemyError
Expand Down Expand Up @@ -245,6 +246,7 @@ def create_product_subscription(
ProductReviewTask(
org_id=org.id,
org_name=org.name,
org_access_type=org.access_type,
product_code=product_subscription.product_code,
product_description=product_model.description,
product_subscription_id=product_subscription.id,
Expand Down Expand Up @@ -371,11 +373,14 @@ def _reset_subscription_and_review_task(
@staticmethod
def _create_review_task(review_task: ProductReviewTask):
task_type = review_task.product_description
action_type = (
TaskAction.QUALIFIED_SUPPLIER_REVIEW.value
if review_task.product_code in QUALIFIED_SUPPLIER_PRODUCT_CODES
else TaskAction.PRODUCT_REVIEW.value
)

required_review_types = {AccessType.GOVM.value, AccessType.GOVN.value}
if review_task.org_access_type in required_review_types:
action_type = TaskAction.NEW_PRODUCT_FEE_REVIEW.value
elif review_task.product_code in QUALIFIED_SUPPLIER_PRODUCT_CODES:
action_type = TaskAction.QUALIFIED_SUPPLIER_REVIEW.value
else:
action_type = TaskAction.PRODUCT_REVIEW.value

task_info = {
"name": review_task.org_name,
Expand All @@ -391,18 +396,39 @@ def _create_review_task(review_task: ProductReviewTask):
"externalSourceId": review_task.external_source_id,
}
TaskService.create_task(task_info, False)

@staticmethod
def has_product_fee(org, product_model):
"""Check if product code exists in pay-api account fees."""
pay_url = current_app.config.get("PAY_API_URL")
# invoke pay-api
token = RestService.get_service_account_token()
response = RestService.get(endpoint=f"{pay_url}/accounts/{org.id}/fees", token=token, retry_on_failure=True)

if response and response.status_code == 200:
response_data = response.json()
account_fees = response_data.get("accountFees", [])
product_code = product_model.code

for fee in account_fees:
if fee.get("product") == product_code:
return True

return False

@staticmethod
def find_subscription_status(org, product_model, auto_approve=False):
"""Return the subscriptions status based on org type."""
# GOVM accounts has default active subscriptions
skip_review_types = [AccessType.GOVM.value]
if product_model.need_review and auto_approve is False:
return (
ProductSubscriptionStatus.ACTIVE.value
if (org.access_type in skip_review_types)
else ProductSubscriptionStatus.PENDING_STAFF_REVIEW.value
)
required_review_types = {AccessType.GOVM.value, AccessType.GOVN.value}

needs_review = (
(org.access_type in required_review_types and Product.has_product_fee(org, product_model))
or (product_model.need_review and not auto_approve)
)

if needs_review:
return ProductSubscriptionStatus.PENDING_STAFF_REVIEW.value

return ProductSubscriptionStatus.ACTIVE.value

@staticmethod
Expand Down
1 change: 1 addition & 0 deletions auth-api/src/auth_api/utils/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ class TaskAction(Enum):
ACCOUNT_REVIEW = "ACCOUNT_REVIEW"
PRODUCT_REVIEW = "PRODUCT_REVIEW"
QUALIFIED_SUPPLIER_REVIEW = "QUALIFIED_SUPPLIER_REVIEW"
NEW_PRODUCT_FEE_REVIEW = "NEW_PRODUCT_FEE_REVIEW"


class ActivityAction(Enum):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,8 @@ export default defineComponent({

if (!state.staffReviewClear && state.addProductOnAccountAdmin) {
state.dialogTitle = 'Staff Review Required'
state.dialogText = `This product needs a review by our staff before it's added to your account.
We'll notify you by email once it's approved.`
state.dialogText = `This product needs a review by our staff before it is added to your account.
You will be notified by email once the request is reviewed.`
confirmDialog.value.open()
} else if (!state.addProductOnAccountAdmin) {
state.displayRemoveProductDialog = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@
{{ formatDate(item.dateSubmitted, 'MMM DD, YYYY') }}
</template>
<template #[`item.type`]="{ item }">
{{ item.relationshipType === TaskRelationshipTypeEnum.PRODUCT ? `Access Request (${item.type})` :
{{ item.action === TaskActionEnum.NEW_PRODUCT_FEE_REVIEW ? 'New Product Fee Review' :
item.relationshipType === TaskRelationshipTypeEnum.PRODUCT ? `Access Request (${item.type})` :
item.type
}}
</template>
Expand Down Expand Up @@ -183,7 +184,7 @@

<script lang="ts">
import { Component, Mixins, Watch } from 'vue-property-decorator'
import { SessionStorageKeys, TaskRelationshipStatus, TaskRelationshipType, TaskStatus } from '@/util/constants'
import { SessionStorageKeys, TaskAction, TaskRelationshipStatus, TaskRelationshipType, TaskStatus } from '@/util/constants'
import { Task, TaskFilterParams, TaskList } from '@/models/Task'
import { mapActions, mapState } from 'pinia'
import { Action } from 'pinia-class'
Expand Down Expand Up @@ -218,6 +219,7 @@ export default class StaffPendingAccountsTable extends Mixins(PaginationMixin) {
private tableDataOptions: Partial<DataOptions> = {}
private isTableLoading: boolean = false
public TaskRelationshipTypeEnum = TaskRelationshipType
public TaskActionEnum = TaskAction
protected searchParamsExist = false
private datePicker = null
private dateTxt = ''
Expand Down Expand Up @@ -268,7 +270,8 @@ export default class StaffPendingAccountsTable extends Mixins(PaginationMixin) {
{ desc: 'New Account', val: 'New Account' },
{ desc: 'BCeID Admin', val: 'BCeID Admin' },
{ desc: 'GovM', val: 'GovM' },
{ desc: 'GovN', val: 'GovN' }
{ desc: 'GovN', val: 'GovN' },
{ desc: 'New Product Review', val: TaskAction.NEW_PRODUCT_FEE_REVIEW }
]

private searchParams: TaskFilterParams = JSON.parse(ConfigHelper.getFromSession(SessionStorageKeys.PendingAccountsSearchFilter)) ||
Expand Down Expand Up @@ -343,6 +346,11 @@ export default class StaffPendingAccountsTable extends Mixins(PaginationMixin) {
if (this.searchParams.status) {
this.taskFilter.statuses = [this.searchParams.status]
}
// If type is NEW_PRODUCT_FEE_REVIEW, convert it to action parameter
if (this.searchParams.type === TaskAction.NEW_PRODUCT_FEE_REVIEW) {
this.taskFilter.action = TaskAction.NEW_PRODUCT_FEE_REVIEW
delete this.taskFilter.type
}

const staffTasksResp = await this.fetchTasks(this.taskFilter)
this.staffTasks = staffTasksResp.tasks
Expand Down
1 change: 1 addition & 0 deletions auth-web/src/models/Organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ export interface OrgPaymentDetails {
futurePaymentMethod: string
revenueAccount?: GLInfo
eftEnable?: boolean
accountFees?: AccountFee[]
}

export interface CFSAccountDetails {
Expand Down
1 change: 1 addition & 0 deletions auth-web/src/models/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface TaskFilterParams {
dateSubmitted?: string;
status?: string
modifiedBy?: string
action?: string
}

export interface TaskList {
Expand Down
3 changes: 3 additions & 0 deletions auth-web/src/services/task.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export default class TaskService {
if (taskFilter.modifiedBy) {
params.append('modifiedBy', taskFilter.modifiedBy)
}
if (taskFilter.action) {
params.append('action', taskFilter.action)
}

return axios.get(`${ConfigHelper.getAuthAPIUrl()}/tasks`, { params })
}
Expand Down
14 changes: 12 additions & 2 deletions auth-web/src/stores/org.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,17 @@ export const useOrgStore = defineStore('org', () => {
canEditBusinessInfo.value
})

function isProductReviewed (code) {
return state.currentAccountFees.some(accountFee => accountFee.product === code)
}

function needStaffReview (code) {
const skipReviewTypes = [AccessType.GOVM]
const requireReviewTypes = [AccessType.GOVM, AccessType.GOVN]
const product = state.productList.find(product => product.code === code)
return !skipReviewTypes.includes(state.currentOrganization?.accessType as AccessType) && product.needReview
if (!isProductReviewed(code) && requireReviewTypes.includes(state.currentOrganization?.accessType as AccessType)) {
return true
}
return !!product?.needReview
}

const isBusinessAccount = computed<boolean>(() => {
Expand Down Expand Up @@ -776,6 +783,9 @@ export const useOrgStore = defineStore('org', () => {
setCurrentOrganizationGLInfo(response?.data?.revenueAccount)
}
state.currentOrgPaymentDetails = response?.data
if (response?.data?.accountFees) {
setCurrentAccountFees(response?.data?.accountFees)
}
return response?.data
}

Expand Down
3 changes: 2 additions & 1 deletion auth-web/src/util/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,8 @@ export enum TaskType {
export enum TaskAction {
AFFIDAVIT_REVIEW = 'AFFIDAVIT_REVIEW',
ACCOUNT_REVIEW = 'ACCOUNT_REVIEW',
PRODUCT_REVIEW = 'PRODUCT_REVIEW'
PRODUCT_REVIEW = 'PRODUCT_REVIEW',
NEW_PRODUCT_FEE_REVIEW = 'NEW_PRODUCT_FEE_REVIEW'
}

export enum FeeCodes {
Expand Down
34 changes: 24 additions & 10 deletions auth-web/src/views/auth/staff/ReviewAccountView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -436,13 +436,14 @@ export default defineComponent({
}

/*
5 types of Tasks:
6 types of Tasks:
1. New BCeId Account -> TaskType.NEW_ACCOUNT_STAFF_REVIEW and TaskRelationshipType.ORG and AFFIDAVIT_REVIEW action
2. Product request -> TaskType.PRODUCT and TaskRelationshipType.PRODUCT and PRODUCT_REVIEW action
3. GovM review -> TaskType.GOVM and TaskRelationshipType.ORG and ACCOUNT_REVIEW action
4. BCeId admin review -> TaskType.BCeID Admin and TaskRelationshipType.USER and ACCOUNT_REVIEW action
5. GovN review -> 1. Bcsc flow: TaskType.GOVN_REVIEW and TaskRelationshipType.ORG and ACCOUNT_REVIEW action
2. Bceid flow: TaskType.GOVN_REVIEW and TaskRelationshipType.ORG and AFFIDAVIT_REVIEW action
6. New Product Fee Review -> TaskType.NEW_PRODUCT_FEE_REVIEW and TaskRelationshipType.PRODUCT and PRODUCT_REVIEW action
*/

const componentList = computed(() => {
Expand Down Expand Up @@ -502,11 +503,19 @@ export default defineComponent({
// Since task of Product type has variable Task Type (eg, Wills Registry, PPR ) we specify in default.
// Also, we double check by task relationship type
if (taskRelationshipType.value === TaskRelationshipType.PRODUCT) {
return [
{ ...componentAccountInformation(1) },
{ ...componentAccountAdministrator(2) },
{ ...componentAgreementInformation(3) }
]
if (task.value?.action === TaskAction.NEW_PRODUCT_FEE_REVIEW) {
return [
{ ...componentAccountInformation(1) },
{ ...componentAccountAdministrator(2) },
{ ...componentProductFee(3) }
]
} else {
return [
{ ...componentAccountInformation(1) },
{ ...componentAccountAdministrator(2) },
{ ...componentAgreementInformation(3) }
]
}
} else {
return []
}
Expand All @@ -519,8 +528,10 @@ export default defineComponent({
taskRelationshipType.value = task.value.relationshipType
await staffStore.syncTaskUnderReview(task.value)

// If the task type is GOVM or GOVN, then need to populate product fee codes
if (task.value.type === TaskType.GOVM_REVIEW || task.value.type === TaskType.GOVN_REVIEW) {
// If the task type is GOVM or GOVN or NEW_PRODUCT_FEE_REVIEW, then need to populate product fee codes
if (task.value.type === TaskType.GOVM_REVIEW ||
task.value.type === TaskType.GOVN_REVIEW ||
task.value.action === TaskAction.NEW_PRODUCT_FEE_REVIEW) {
const accountId = task.value.relationshipId
await orgStore.fetchCurrentOrganizationGLInfo(accountId)
await orgStore.fetchOrgProductFeeCodes()
Expand Down Expand Up @@ -567,7 +578,9 @@ export default defineComponent({
return false
}

if (task.value.type === TaskType.GOVM_REVIEW && !productFeeFormValid.value) {
if ((task.value.type === TaskType.GOVM_REVIEW ||
task.value.action === TaskAction.NEW_PRODUCT_FEE_REVIEW) &&
!productFeeFormValid.value) {
// validate form before showing pop-up
(productFeeRef.value[0] as any).validateNow()
if (!productFeeFormValid.value) {
Expand Down Expand Up @@ -623,7 +636,8 @@ export default defineComponent({
const taskType: any = task.value.type

if (
[TaskType.GOVM_REVIEW, TaskType.GOVN_REVIEW].includes(taskType) &&
([TaskType.GOVM_REVIEW, TaskType.GOVN_REVIEW].includes(taskType) ||
task.value.action === TaskAction.NEW_PRODUCT_FEE_REVIEW) &&
(!accountInfoAccessType.value || [AccessType.GOVN, AccessType.GOVM].includes(accountInfoAccessType.value))
) {
await orgStore.createAccountFees(task.value.relationshipId)
Expand Down
Loading