Skip to content

Commit

Permalink
Merge pull request #4258 from unicef/201639_Individuals_Add_a_new_sec…
Browse files Browse the repository at this point in the history
…tion_to_Display_Delivery_mechanism_details_for_an_individual

201639_Individuals_Add_a_new_section_to_Display_Delivery_mechanism_d…
  • Loading branch information
mmaciekk authored Sep 26, 2024
2 parents fef8aa6 + 1067882 commit 8d4befd
Show file tree
Hide file tree
Showing 11 changed files with 579 additions and 63 deletions.
53 changes: 53 additions & 0 deletions src/frontend/data/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,52 @@ type DeliveredQuantityNode {
currency: String
}

enum DeliveryMechanismDataDeliveryMechanismChoice {
CARDLESS_CASH_WITHDRAWAL
CASH
CASH_BY_FSP
CHEQUE
DEPOSIT_TO_CARD
MOBILE_MONEY
PRE_PAID_CARD
REFERRAL
TRANSFER
TRANSFER_TO_ACCOUNT
VOUCHER
ATM_CARD
CASH_OVER_THE_COUNTER
TRANSFER_TO_DIGITAL_WALLET
}

type DeliveryMechanismDataNode implements Node {
id: ID!
rdiMergeStatus: DeliveryMechanismDataRdiMergeStatus!
createdAt: DateTime!
updatedAt: DateTime!
individual: IndividualNode!
deliveryMechanismChoice: DeliveryMechanismDataDeliveryMechanismChoice
deliveryMechanism: DeliveryMechanismNode!
data: JSONString!
isValid: Boolean
validationErrors: JSONString!
possibleDuplicateOf: DeliveryMechanismDataNode
possibleDuplicates(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismDataNodeConnection!
name: String
individualTabData: JSONString
}

type DeliveryMechanismDataNodeConnection {
pageInfo: PageInfo!
edges: [DeliveryMechanismDataNodeEdge]!
totalCount: Int
edgeCount: Int
}

type DeliveryMechanismDataNodeEdge {
node: DeliveryMechanismDataNode
cursor: String!
}

input DeliveryMechanismDataObjectType {
label: String!
approveStatus: Boolean!
Expand All @@ -965,6 +1011,11 @@ input DeliveryMechanismDataPayloadFieldObjectType {
previousValue: String
}

enum DeliveryMechanismDataRdiMergeStatus {
PENDING
MERGED
}

type DeliveryMechanismNode implements Node {
id: ID!
createdAt: DateTime!
Expand All @@ -981,6 +1032,7 @@ type DeliveryMechanismNode implements Node {
deliverymechanismperpaymentplanSet(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismPerPaymentPlanNodeConnection!
paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection!
paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection!
deliverymechanismdataSet(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismDataNodeConnection!
}

type DeliveryMechanismNodeConnection {
Expand Down Expand Up @@ -2511,6 +2563,7 @@ type IndividualNode implements Node {
paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection!
collectorPayments(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection!
paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection!
deliveryMechanismsData: [DeliveryMechanismDataNode]
complaintTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketComplaintDetailsNodeConnection!
sensitiveTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketSensitiveDetailsNodeConnection!
individualDataUpdateTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketIndividualDataUpdateDetailsNodeConnection!
Expand Down
142 changes: 133 additions & 9 deletions src/frontend/src/__generated__/graphql.tsx

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/frontend/src/__generated__/introspection-result.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/frontend/src/apollo/fragments/IndividualFragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ export const individualDetailed = gql`
accountHolderName
bankBranchName
}
deliveryMechanismsData {
name
isValid
individualTabData
}
documents {
edges {
node {
Expand Down
1 change: 1 addition & 0 deletions src/hct_mis_api/apps/account/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def _generate_next_value_( # type: ignore # https://github.com/python/mypy/issu
POPULATION_VIEW_HOUSEHOLDS_DETAILS = auto()
POPULATION_VIEW_INDIVIDUALS_LIST = auto()
POPULATION_VIEW_INDIVIDUALS_DETAILS = auto()
POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION = auto()

# Programme
PROGRAMME_VIEW_LIST_AND_DETAILS = auto()
Expand Down
33 changes: 33 additions & 0 deletions src/hct_mis_api/apps/household/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
from hct_mis_api.apps.household.services.household_programs_with_delivered_quantity import (
delivered_quantity_service,
)
from hct_mis_api.apps.payment.models import DeliveryMechanismData
from hct_mis_api.apps.payment.utils import get_payment_items_for_dashboard
from hct_mis_api.apps.program.models import Program
from hct_mis_api.apps.registration_data.nodes import DeduplicationResultNode
Expand Down Expand Up @@ -204,6 +205,26 @@ class Meta:
connection_class = ExtendedConnection


class DeliveryMechanismDataNode(BaseNodePermissionMixin, DjangoObjectType):
permission_classes = (hopePermissionClass(Permissions.POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION),)

name = graphene.String(required=False)
is_valid = graphene.Boolean()
individual_tab_data = graphene.JSONString()

def resolve_name(self, info: Any) -> str:
return self.delivery_mechanism.name

def resolve_individual_tab_data(self, info: Any) -> dict:
return {key: self._data.get(key, None) for key in self.all_dm_fields}

class Meta:
model = DeliveryMechanismData
exclude = ("unique_key", "signature_hash")
interfaces = (relay.Node,)
connection_class = ExtendedConnection


class IndividualNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectType):
permission_classes: Tuple[Type[BasePermission], ...] = (
hopePermissionClass(Permissions.POPULATION_VIEW_INDIVIDUALS_DETAILS),
Expand All @@ -230,6 +251,7 @@ class IndividualNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectTyp
phone_no_alternative_valid = graphene.Boolean()
payment_channels = graphene.List(BankAccountInfoNode)
preferred_language = graphene.String()
delivery_mechanisms_data = graphene.List(DeliveryMechanismDataNode)

@staticmethod
def resolve_preferred_language(parent: Individual, info: Any) -> Optional[str]:
Expand Down Expand Up @@ -287,6 +309,17 @@ def resolve_phone_no_valid(parent, info: Any) -> Boolean:
def resolve_phone_no_alternative_valid(parent, info: Any) -> Boolean:
return parent.phone_no_alternative_valid

def resolve_delivery_mechanisms_data(parent, info: Any) -> QuerySet[DeliveryMechanismData]:
program_id = get_program_id_from_headers(info.context.headers)
if not info.context.user.has_permission(
Permissions.POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION.value,
parent.business_area,
program_id,
):
return parent.delivery_mechanisms_data.none()

return parent.delivery_mechanisms_data.all()

@classmethod
def check_node_permission(cls, info: Any, object_instance: Individual) -> None:
super().check_node_permission(info, object_instance)
Expand Down
1 change: 1 addition & 0 deletions src/hct_mis_api/apps/payment/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ class DeliveryMechanismDataAdmin(HOPEModelAdminBase):
list_display = ("individual", "delivery_mechanism", "is_valid")
raw_id_fields = ("individual", "possible_duplicate_of")
readonly_fields = ("possible_duplicate_of", "unique_key", "signature_hash", "validation_errors")
exclude = ("delivery_mechanism_choice",)


@admin.register(DeliveryMechanism)
Expand Down
18 changes: 18 additions & 0 deletions src/hct_mis_api/apps/payment/migrations/0144_migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.25 on 2024-09-23 14:12

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('payment', '0143_migration'),
]

operations = [
migrations.AlterField(
model_name='deliverymechanismdata',
name='delivery_mechanism_choice',
field=models.CharField(blank=True, choices=[('Cardless cash withdrawal', 'Cardless cash withdrawal'), ('Cash', 'Cash'), ('Cash by FSP', 'Cash by FSP'), ('Cheque', 'Cheque'), ('Deposit to Card', 'Deposit to Card'), ('Mobile Money', 'Mobile Money'), ('Pre-paid card', 'Pre-paid card'), ('Referral', 'Referral'), ('Transfer', 'Transfer'), ('Transfer to Account', 'Transfer to Account'), ('Voucher', 'Voucher'), ('ATM Card', 'ATM Card'), ('Cash over the counter', 'Cash over the counter'), ('Transfer to Digital Wallet', 'Transfer to Digital Wallet')], max_length=255, null=True, verbose_name='Delivery Mechanism'),
),
]
16 changes: 14 additions & 2 deletions src/hct_mis_api/apps/payment/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2255,7 +2255,11 @@ class DeliveryMechanismData(MergeStatusModel, TimeStampedUUIDModel, SignatureMix
"household.Individual", on_delete=models.CASCADE, related_name="delivery_mechanisms_data"
)
delivery_mechanism_choice = models.CharField(
max_length=255, verbose_name=_("Delivery Mechanism"), choices=DeliveryMechanismChoices.DELIVERY_TYPE_CHOICES
max_length=255,
verbose_name=_("Delivery Mechanism"),
choices=DeliveryMechanismChoices.DELIVERY_TYPE_CHOICES,
null=True,
blank=True,
) # TODO MB drop later
delivery_mechanism = models.ForeignKey("DeliveryMechanism", on_delete=models.PROTECT)
data = JSONField(default=dict, blank=True)
Expand Down Expand Up @@ -2300,10 +2304,14 @@ def get_associated_object(self, associated_with: str) -> Any:
associated_objects = {
_INDIVIDUAL: self.individual,
_HOUSEHOLD: self.individual.household,
_DELIVERY_MECHANISM_DATA: json.loads(self.data) if not isinstance(self.data, dict) else self.data,
_DELIVERY_MECHANISM_DATA: self._data,
}
return associated_objects.get(associated_with)

@property
def _data(self) -> Dict:
return json.loads(self.data) if not isinstance(self.data, dict) else self.data

@cached_property
def delivery_data(self) -> Dict:
delivery_data = {}
Expand Down Expand Up @@ -2373,6 +2381,10 @@ def delivery_mechanism_required_fields_definitions(self) -> List[dict]:
def all_fields(self) -> List[dict]:
return self.delivery_mechanism.all_fields

@property
def all_dm_fields(self) -> List[dict]:
return self.delivery_mechanism.all_dm_fields

@property
def unique_fields(self) -> List[str]:
return self.delivery_mechanism.unique_fields
Expand Down
38 changes: 38 additions & 0 deletions tests/unit/apps/household/snapshots/snap_test_individual_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,44 @@
]
}

snapshots['TestIndividualWithDeliveryMechanismsDataQuery::test_individual_query_delivery_mechanisms_data_0_with_permissions 1'] = {
'data': {
'individual': {
'birthDate': '1943-07-30',
'deliveryMechanismsData': [
{
'individualTabData': '{"card_number__atm_card": "123", "card_expiry_date__atm_card": "2022-01-01", "name_of_cardholder__atm_card": "Marek"}',
'isValid': True,
'name': 'ATM Card'
},
{
'individualTabData': '{"delivery_phone_number__mobile_money": "123456789", "provider__mobile_money": "Provider", "service_provider_code__mobile_money": "ABC"}',
'isValid': False,
'name': 'Mobile Money'
}
],
'familyName': 'Butler',
'fullName': 'Benjamin Butler',
'givenName': 'Benjamin',
'phoneNo': '(953)682-4596'
}
}
}

snapshots['TestIndividualWithDeliveryMechanismsDataQuery::test_individual_query_delivery_mechanisms_data_1_without_permissions 1'] = {
'data': {
'individual': {
'birthDate': '1943-07-30',
'deliveryMechanismsData': [
],
'familyName': 'Butler',
'fullName': 'Benjamin Butler',
'givenName': 'Benjamin',
'phoneNo': '(953)682-4596'
}
}
}

snapshots['TestIndividualWithFlexFieldsQuery::test_individual_query_single_with_flex_fields 1'] = {
'data': {
'individual': {
Expand Down
Loading

0 comments on commit 8d4befd

Please sign in to comment.