diff --git a/back/boxtribute_server/business_logic/statistics/crud.py b/back/boxtribute_server/business_logic/statistics/crud.py index ad548a0d6..88d85f5e6 100644 --- a/back/boxtribute_server/business_logic/statistics/crud.py +++ b/back/boxtribute_server/business_logic/statistics/crud.py @@ -8,7 +8,7 @@ from peewee import JOIN, SQL, fn from ...db import execute_sql -from ...enums import BoxState, TaggableObjectType, TargetType +from ...enums import BeneficiaryReachType, BoxState, TaggableObjectType, TargetType from ...errors import InvalidDate from ...models.definitions.base import Base from ...models.definitions.beneficiary import Beneficiary @@ -18,13 +18,14 @@ from ...models.definitions.organisation import Organisation from ...models.definitions.product import Product from ...models.definitions.product_category import ProductCategory +from ...models.definitions.services_relation import ServicesRelation from ...models.definitions.shareable_link import ShareableLink from ...models.definitions.size import Size from ...models.definitions.size_range import SizeRange from ...models.definitions.tag import Tag from ...models.definitions.tags_relation import TagsRelation from ...models.definitions.transaction import Transaction -from ...models.utils import compute_age, convert_ids, utcnow +from ...models.utils import HISTORY_DELETION_MESSAGE, compute_age, convert_ids, utcnow from ...utils import in_ci_environment, in_production_environment from ..metrics.crud import exclude_test_organisation from .sql import MOVED_BOXES_QUERY, STOCK_OVERVIEW_QUERY @@ -92,6 +93,31 @@ def _generate_dimensions(*names, facts): .dicts() ) + if "beneficiary" in names: + beneficiary_ids = {f["beneficiary_id"] for f in facts} + age = fn.IF( + Beneficiary.date_of_birth > 0, compute_age(Beneficiary.date_of_birth), None + ) + dimensions["beneficiary"] = ( + Beneficiary.select( + Beneficiary.id, + age.alias("age"), + Beneficiary.gender, + fn.GROUP_CONCAT(TagsRelation.tag.distinct()).alias("tag_ids"), + ) + .left_outer_join( + TagsRelation, + on=( + (TagsRelation.object_id == Beneficiary.id) + & (TagsRelation.object_type == TaggableObjectType.Beneficiary) + & (TagsRelation.deleted_on.is_null()) + ), + ) + .where(Beneficiary.id << beneficiary_ids) + .group_by(Beneficiary.id) + .dicts() + ) + if "target" in names: dimensions["target"] = [] for target_type in TargetType: @@ -180,6 +206,134 @@ def compute_beneficiary_demographics(base_id): ) +def compute_beneficiary_reach(base_id): + """Obtain data about beneficiary interactions (categorized by BeneficiaryReachType). + Related to the reachedBeneficiariesNumbers metric which is an aggregated form of + this data. + """ + _validate_existing_base(base_id) + FamilyMember = Beneficiary.alias() + Interactions = ( + # created/edited (persistently logged in history table) + DbChangeHistory.select( + fn.DATE(DbChangeHistory.change_date).alias("reached_on"), + DbChangeHistory.record_id.alias("beneficiary_id"), + SQL(f"{BeneficiaryReachType.CreatedOrEdited.value}").alias("reach_type"), + ) + .join( + Beneficiary, + on=( + (Beneficiary.id == DbChangeHistory.record_id) + & (Beneficiary.base == base_id) + ), + ) + .where( + DbChangeHistory.table_name == Beneficiary._meta.table_name, + # Exclude "Record deleted [by dailyroutine|without undelete]" + ~DbChangeHistory.changes.startswith(HISTORY_DELETION_MESSAGE), + ) + | ( + # created acc. to people table (contains info for some beneficiaries + # directly imported to the DB but misses permanently deleted beneficiaries) + Beneficiary.select( + fn.DATE(Beneficiary.created_on).alias("reached_on"), + Beneficiary.id.alias("beneficiary_id"), + SQL(f"{BeneficiaryReachType.CreatedOrEdited.value}").alias( + "reach_type" + ), + ).where(Beneficiary.base == base_id) + ) + | ( + # involved in transactions (family heads) + Transaction.select( + fn.DATE(Transaction.created_on).alias("reached_on"), + Transaction.beneficiary.alias("beneficiary_id"), + SQL(f"{BeneficiaryReachType.Checkout.value}").alias("reach_type"), + ).join( + Beneficiary, + on=( + (Beneficiary.id == Transaction.beneficiary) + & (Beneficiary.base == base_id) + & (Transaction.count > 0) + ), + ), + ) + | ( + # Family members of a beneficiary who had a transaction are also + # counted as reached via "Checkout" on the same date + Transaction.select( + fn.DATE(Transaction.created_on).alias("reached_on"), + FamilyMember.id.alias("beneficiary_id"), + SQL(f"{BeneficiaryReachType.Checkout.value}").alias("reach_type"), + ) + .join( + Beneficiary, + on=( + (Beneficiary.id == Transaction.beneficiary) + & (Beneficiary.base == base_id) + & (Transaction.count > 0) + ), + ) + .join( + FamilyMember, + on=(FamilyMember.family_head == Transaction.beneficiary), + ), + ) + | ( + ServicesRelation.select( + fn.DATE(ServicesRelation.created_on).alias("reached_on"), + ServicesRelation.beneficiary.alias("beneficiary_id"), + SQL(f"{BeneficiaryReachType.ServiceUsed.value}").alias("reach_type"), + ).join( + Beneficiary, + on=( + (Beneficiary.id == ServicesRelation.beneficiary) + & (Beneficiary.base == base_id) + ), + ), + ) + | ( + TagsRelation.select( + fn.DATE(TagsRelation.created_on).alias("reached_on"), + TagsRelation.object_id.alias("beneficiary_id"), + SQL(f"{BeneficiaryReachType.TagApplied.value}").alias("reach_type"), + ) + .join( + Beneficiary, + on=( + (TagsRelation.object_type == TaggableObjectType.Beneficiary) + & (Beneficiary.id == TagsRelation.object_id) + & (Beneficiary.base == base_id) + ), + ) + .where(TagsRelation.created_on.is_null(False)) + ) + ) + facts = ( + Beneficiary.select( + Interactions.c.reached_on, + Interactions.c.beneficiary_id, + Interactions.c.reach_type, + fn.COUNT(Interactions.c.beneficiary_id).alias("count"), + ) + .from_(Interactions) + .group_by( + SQL("reached_on"), + SQL("beneficiary_id"), + SQL("reach_type"), + ) + .dicts() + .execute() + ) + for fact in facts: + fact["reach_type"] = BeneficiaryReachType(fact["reach_type"]) + dimensions = _generate_dimensions("beneficiary", facts=facts) + for b in dimensions["beneficiary"]: + b["tag_ids"] = sorted(convert_ids(b["tag_ids"])) + dimensions.update(_generate_dimensions("tag", facts=dimensions["beneficiary"])) + return DataCube(facts=facts, dimensions=dimensions, type="BeneficiaryReachData") + + def compute_created_boxes(base_id): """For each combination of product ID, category ID, gender, and day-truncated creation date count the number of created boxes, and the contained items, in the diff --git a/back/boxtribute_server/business_logic/statistics/queries.py b/back/boxtribute_server/business_logic/statistics/queries.py index 97c399a0f..4ab88e1d2 100644 --- a/back/boxtribute_server/business_logic/statistics/queries.py +++ b/back/boxtribute_server/business_logic/statistics/queries.py @@ -10,6 +10,7 @@ from . import query from .crud import ( compute_beneficiary_demographics, + compute_beneficiary_reach, compute_created_boxes, compute_moved_boxes, compute_stock_overview, @@ -29,6 +30,15 @@ def resolve_beneficiary_demographics(*_, base_id): return compute_beneficiary_demographics(base_id) +@query.field("beneficiaryReach") +@use_db_replica +def resolve_beneficiary_reach(*_, base_id): + # No cross-organisational access for beneficiary-related data + authorize(permission="beneficiary:read", base_id=base_id) + authorize(permission="tag_relation:read") + return compute_beneficiary_reach(base_id) + + @query.field("createdBoxes") @use_db_replica def resolve_created_boxes(*_, base_id): diff --git a/back/boxtribute_server/enums.py b/back/boxtribute_server/enums.py index 2a95ce252..0c75a9001 100644 --- a/back/boxtribute_server/enums.py +++ b/back/boxtribute_server/enums.py @@ -134,6 +134,13 @@ class TargetType(enum.IntEnum): BoxState = enum.auto() +class BeneficiaryReachType(enum.IntEnum): + CreatedOrEdited = 1 + TagApplied = enum.auto() + ServiceUsed = enum.auto() + Checkout = enum.auto() + + class ProductTypeFilter(enum.Enum): Custom = "Custom" StandardInstantiation = "StandardInstantiation" diff --git a/back/boxtribute_server/graph_ql/definitions/basic/enums.graphql b/back/boxtribute_server/graph_ql/definitions/basic/enums.graphql index d110915c9..38cd2dacf 100644 --- a/back/boxtribute_server/graph_ql/definitions/basic/enums.graphql +++ b/back/boxtribute_server/graph_ql/definitions/basic/enums.graphql @@ -126,6 +126,13 @@ enum ShareableView { StockOverview } +enum BeneficiaryReachType { + CreatedOrEdited + TagApplied + ServiceUsed + Checkout +} + """ TODO: Add description here once specs are final/confirmed """ diff --git a/back/boxtribute_server/graph_ql/definitions/basic/types.graphql b/back/boxtribute_server/graph_ql/definitions/basic/types.graphql index 5217136ab..26d0d327a 100644 --- a/back/boxtribute_server/graph_ql/definitions/basic/types.graphql +++ b/back/boxtribute_server/graph_ql/definitions/basic/types.graphql @@ -3,8 +3,8 @@ interface DataCube { dimensions: Dimensions } -union Result = BeneficiaryDemographicsResult | CreatedBoxesResult | TopProductsCheckedOutResult | TopProductsDonatedResult | MovedBoxesResult | StockOverviewResult -union Dimensions = BeneficiaryDemographicsDimensions | CreatedBoxDataDimensions | TopProductsDimensions | MovedBoxDataDimensions | StockOverviewDataDimensions +union Result = BeneficiaryDemographicsResult | CreatedBoxesResult | TopProductsCheckedOutResult | TopProductsDonatedResult | MovedBoxesResult | StockOverviewResult | BeneficiaryReachResult +union Dimensions = BeneficiaryDemographicsDimensions | CreatedBoxDataDimensions | TopProductsDimensions | MovedBoxDataDimensions | StockOverviewDataDimensions | BeneficiaryReachDimensions type BeneficiaryDemographicsData implements DataCube { facts: [BeneficiaryDemographicsResult] @@ -24,6 +24,30 @@ type BeneficiaryDemographicsResult { count: Int } +type BeneficiaryReachData implements DataCube { + facts: [BeneficiaryReachResult!]! + dimensions: BeneficiaryReachDimensions! +} + +type BeneficiaryReachResult { + reachedOn: Date! + beneficiaryId: Int! + reachType: BeneficiaryReachType + count: Int! +} + +type BeneficiaryReachDimensions { + beneficiary: [BeneficiaryDimensionInfo!] + tag: [TagDimensionInfo!] +} + +type BeneficiaryDimensionInfo { + id: Int! + age: Int + tagIds: [Int!] + gender: HumanGender! +} + type CreatedBoxesData implements DataCube { facts: [CreatedBoxesResult] dimensions: CreatedBoxDataDimensions diff --git a/back/boxtribute_server/graph_ql/definitions/protected/queries.graphql b/back/boxtribute_server/graph_ql/definitions/protected/queries.graphql index 3a8d6db23..de52e6eab 100644 --- a/back/boxtribute_server/graph_ql/definitions/protected/queries.graphql +++ b/back/boxtribute_server/graph_ql/definitions/protected/queries.graphql @@ -60,6 +60,7 @@ type Query { metrics(organisationId: ID): Metrics beneficiaryDemographics(baseId: Int!): BeneficiaryDemographicsData + beneficiaryReach(baseId: Int!): BeneficiaryReachData createdBoxes(baseId: Int!): CreatedBoxesData topProductsCheckedOut(baseId: Int!): TopProductsCheckedOutData topProductsDonated(baseId: Int!): TopProductsDonatedData diff --git a/back/boxtribute_server/graph_ql/enums.py b/back/boxtribute_server/graph_ql/enums.py index 0ba141ea9..885c0d563 100644 --- a/back/boxtribute_server/graph_ql/enums.py +++ b/back/boxtribute_server/graph_ql/enums.py @@ -4,6 +4,7 @@ from ariadne import EnumType from ..enums import ( + BeneficiaryReachType, BoxState, DistributionEventState, DistributionEventsTrackingGroupState, @@ -36,6 +37,7 @@ TagType, EnumType("TaggableResourceType", TaggableObjectType), TargetType, + BeneficiaryReachType, TransferAgreementState, TransferAgreementType, ProductType, diff --git a/back/test/endpoint_tests/test_statistics.py b/back/test/endpoint_tests/test_statistics.py index ea2592515..5e910031c 100644 --- a/back/test/endpoint_tests/test_statistics.py +++ b/back/test/endpoint_tests/test_statistics.py @@ -6,7 +6,13 @@ get_data_for_number_of_moved_boxes, number_of_boxes_moved_between, ) -from boxtribute_server.enums import BoxState, ProductGender, TargetType +from boxtribute_server.enums import ( + BeneficiaryReachType, + BoxState, + HumanGender, + ProductGender, + TargetType, +) from boxtribute_server.models.definitions.box import Box from boxtribute_server.models.definitions.location import Location from boxtribute_server.models.utils import compute_age @@ -71,6 +77,131 @@ def test_query_beneficiary_demographics( } +def test_query_beneficiary_reach(client, default_beneficiaries): + query = """query { beneficiaryReach(baseId: 1) { + facts { reachedOn beneficiaryId reachType count } + dimensions { + beneficiary { id age gender tagIds } + tag { id } + } } }""" + data = assert_successful_request(client, query, endpoint="graphql") + age = compute_age(default_beneficiaries[0]["date_of_birth"]) + ages = [age, None, None, None, age] + all_tag_ids = [[1, 3], [], [], [], [1]] + assert data == { + "dimensions": { + "beneficiary": [ + { + "age": age, + "gender": (b.get("gender") or HumanGender.Diverse).name, + "id": b["id"], + "tagIds": tag_ids, + } + for b, age, tag_ids in zip(default_beneficiaries, ages, all_tag_ids) + ], + "tag": [{"id": 1}, {"id": 3}], + }, + "facts": [ + { + "beneficiaryId": 1, + "count": 1, + "reachType": BeneficiaryReachType.CreatedOrEdited.name, + "reachedOn": "2020-06-30", + }, + { + "beneficiaryId": 2, + "count": 1, + "reachType": BeneficiaryReachType.CreatedOrEdited.name, + "reachedOn": "2021-06-30", + }, + { + "beneficiaryId": 3, + "count": 1, + "reachType": BeneficiaryReachType.CreatedOrEdited.name, + "reachedOn": "2022-01-30", + }, + { + "beneficiaryId": 5, + "count": 1, + "reachType": BeneficiaryReachType.CreatedOrEdited.name, + "reachedOn": "2021-06-30", + }, + { + "beneficiaryId": 6, + "count": 1, + "reachType": BeneficiaryReachType.CreatedOrEdited.name, + "reachedOn": "2026-05-31", + }, + { + "beneficiaryId": 1, + "count": 1, + "reachType": BeneficiaryReachType.Checkout.name, + "reachedOn": "2021-01-15", + }, + { + "beneficiaryId": 1, + "count": 1, + "reachType": BeneficiaryReachType.Checkout.name, + "reachedOn": "2020-01-15", + }, + { + "beneficiaryId": 3, + "count": 1, + "reachType": BeneficiaryReachType.Checkout.name, + "reachedOn": "2020-01-15", + }, + { + "beneficiaryId": 3, + "count": 1, + "reachType": BeneficiaryReachType.Checkout.name, + "reachedOn": "2019-01-15", + }, + { + "beneficiaryId": 2, + "count": 1, + "reachType": BeneficiaryReachType.Checkout.name, + "reachedOn": "2021-01-15", + }, + { + "beneficiaryId": 5, + "count": 1, + "reachType": BeneficiaryReachType.Checkout.name, + "reachedOn": "2021-01-15", + }, + { + "beneficiaryId": 2, + "count": 1, + "reachType": BeneficiaryReachType.Checkout.name, + "reachedOn": "2020-01-15", + }, + { + "beneficiaryId": 5, + "count": 1, + "reachType": BeneficiaryReachType.Checkout.name, + "reachedOn": "2020-01-15", + }, + { + "beneficiaryId": 1, + "count": 1, + "reachType": BeneficiaryReachType.ServiceUsed.name, + "reachedOn": "2025-11-24", + }, + { + "beneficiaryId": 1, + "count": 1, + "reachType": BeneficiaryReachType.TagApplied.name, + "reachedOn": "2022-01-01", + }, + { + "beneficiaryId": 6, + "count": 1, + "reachType": BeneficiaryReachType.TagApplied.name, + "reachedOn": "2023-01-01", + }, + ], + } + + def test_query_created_boxes( client, base1_undeleted_products, product_categories, tags ): diff --git a/graphql/generated/graphql-env.d.ts b/graphql/generated/graphql-env.d.ts index 59fa66a15..bea7419a2 100644 --- a/graphql/generated/graphql-env.d.ts +++ b/graphql/generated/graphql-env.d.ts @@ -12,7 +12,12 @@ export type introspection_types = { 'BeneficiaryDemographicsData': { kind: 'OBJECT'; name: 'BeneficiaryDemographicsData'; fields: { 'dimensions': { name: 'dimensions'; type: { kind: 'OBJECT'; name: 'BeneficiaryDemographicsDimensions'; ofType: null; } }; 'facts': { name: 'facts'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'BeneficiaryDemographicsResult'; ofType: null; }; } }; }; }; 'BeneficiaryDemographicsDimensions': { kind: 'OBJECT'; name: 'BeneficiaryDemographicsDimensions'; fields: { 'tag': { name: 'tag'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'TagDimensionInfo'; ofType: null; }; } }; }; }; 'BeneficiaryDemographicsResult': { kind: 'OBJECT'; name: 'BeneficiaryDemographicsResult'; fields: { 'age': { name: 'age'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'count': { name: 'count'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'createdOn': { name: 'createdOn'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; } }; 'deletedOn': { name: 'deletedOn'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; } }; 'gender': { name: 'gender'; type: { kind: 'ENUM'; name: 'HumanGender'; ofType: null; } }; 'tagIds': { name: 'tagIds'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; } }; }; }; + 'BeneficiaryDimensionInfo': { kind: 'OBJECT'; name: 'BeneficiaryDimensionInfo'; fields: { 'age': { name: 'age'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'gender': { name: 'gender'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'HumanGender'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'tagIds': { name: 'tagIds'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; } }; }; }; 'BeneficiaryPage': { kind: 'OBJECT'; name: 'BeneficiaryPage'; fields: { 'elements': { name: 'elements'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; }; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; 'totalCount': { name: 'totalCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; }; }; + 'BeneficiaryReachData': { kind: 'OBJECT'; name: 'BeneficiaryReachData'; fields: { 'dimensions': { name: 'dimensions'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BeneficiaryReachDimensions'; ofType: null; }; } }; 'facts': { name: 'facts'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BeneficiaryReachResult'; ofType: null; }; }; }; } }; }; }; + 'BeneficiaryReachDimensions': { kind: 'OBJECT'; name: 'BeneficiaryReachDimensions'; fields: { 'beneficiary': { name: 'beneficiary'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BeneficiaryDimensionInfo'; ofType: null; }; }; } }; 'tag': { name: 'tag'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TagDimensionInfo'; ofType: null; }; }; } }; }; }; + 'BeneficiaryReachResult': { kind: 'OBJECT'; name: 'BeneficiaryReachResult'; fields: { 'beneficiaryId': { name: 'beneficiaryId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'count': { name: 'count'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'reachType': { name: 'reachType'; type: { kind: 'ENUM'; name: 'BeneficiaryReachType'; ofType: null; } }; 'reachedOn': { name: 'reachedOn'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Date'; ofType: null; }; } }; }; }; + 'BeneficiaryReachType': { name: 'BeneficiaryReachType'; enumValues: 'CreatedOrEdited' | 'TagApplied' | 'ServiceUsed' | 'Checkout'; }; 'BeneficiaryUpdateInput': { kind: 'INPUT_OBJECT'; name: 'BeneficiaryUpdateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'firstName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'lastName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'groupIdentifier'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'dateOfBirth'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; }; defaultValue: null }, { name: 'comment'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'gender'; type: { kind: 'ENUM'; name: 'HumanGender'; ofType: null; }; defaultValue: null }, { name: 'languages'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'Language'; ofType: null; }; }; }; defaultValue: null }, { name: 'familyHeadId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'isVolunteer'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; defaultValue: null }, { name: 'registered'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; defaultValue: null }, { name: 'signature'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'dateOfSignature'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; }; defaultValue: null }]; }; 'Boolean': unknown; 'Box': { kind: 'OBJECT'; name: 'Box'; fields: { 'comment': { name: 'comment'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'createdBy': { name: 'createdBy'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'createdOn': { name: 'createdOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'deletedOn': { name: 'deletedOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'displayUnit': { name: 'displayUnit'; type: { kind: 'OBJECT'; name: 'Unit'; ofType: null; } }; 'distributionEvent': { name: 'distributionEvent'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'history': { name: 'history'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'HistoryEntry'; ofType: null; }; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'labelIdentifier': { name: 'labelIdentifier'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'lastModifiedBy': { name: 'lastModifiedBy'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'lastModifiedOn': { name: 'lastModifiedOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'location': { name: 'location'; type: { kind: 'INTERFACE'; name: 'Location'; ofType: null; } }; 'measureValue': { name: 'measureValue'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'numberOfItems': { name: 'numberOfItems'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'product': { name: 'product'; type: { kind: 'OBJECT'; name: 'Product'; ofType: null; } }; 'qrCode': { name: 'qrCode'; type: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; } }; 'shipmentDetail': { name: 'shipmentDetail'; type: { kind: 'OBJECT'; name: 'ShipmentDetail'; ofType: null; } }; 'size': { name: 'size'; type: { kind: 'OBJECT'; name: 'Size'; ofType: null; } }; 'sourceBox': { name: 'sourceBox'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'state': { name: 'state'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'BoxState'; ofType: null; }; } }; 'tags': { name: 'tags'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Tag'; ofType: null; }; }; } }; }; }; @@ -40,7 +45,7 @@ export type introspection_types = { 'CreatedBoxesResult': { kind: 'OBJECT'; name: 'CreatedBoxesResult'; fields: { 'boxesCount': { name: 'boxesCount'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'categoryId': { name: 'categoryId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'createdOn': { name: 'createdOn'; type: { kind: 'SCALAR'; name: 'Date'; ofType: null; } }; 'gender': { name: 'gender'; type: { kind: 'ENUM'; name: 'ProductGender'; ofType: null; } }; 'itemsCount': { name: 'itemsCount'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'productId': { name: 'productId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'tagIds': { name: 'tagIds'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; } }; }; }; 'CustomProductCreationInput': { kind: 'INPUT_OBJECT'; name: 'CustomProductCreationInput'; isOneOf: false; inputFields: [{ name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'categoryId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'sizeRangeId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'gender'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'ProductGender'; ofType: null; }; }; defaultValue: null }, { name: 'baseId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'price'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'comment'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'inShop'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; defaultValue: null }]; }; 'CustomProductEditInput': { kind: 'INPUT_OBJECT'; name: 'CustomProductEditInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'categoryId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'sizeRangeId'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'gender'; type: { kind: 'ENUM'; name: 'ProductGender'; ofType: null; }; defaultValue: null }, { name: 'price'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'comment'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'inShop'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; defaultValue: null }]; }; - 'DataCube': { kind: 'INTERFACE'; name: 'DataCube'; fields: { 'dimensions': { name: 'dimensions'; type: { kind: 'UNION'; name: 'Dimensions'; ofType: null; } }; 'facts': { name: 'facts'; type: { kind: 'LIST'; name: never; ofType: { kind: 'UNION'; name: 'Result'; ofType: null; }; } }; }; possibleTypes: 'BeneficiaryDemographicsData' | 'CreatedBoxesData' | 'MovedBoxesData' | 'StockOverviewData' | 'TopProductsCheckedOutData' | 'TopProductsDonatedData'; }; + 'DataCube': { kind: 'INTERFACE'; name: 'DataCube'; fields: { 'dimensions': { name: 'dimensions'; type: { kind: 'UNION'; name: 'Dimensions'; ofType: null; } }; 'facts': { name: 'facts'; type: { kind: 'LIST'; name: never; ofType: { kind: 'UNION'; name: 'Result'; ofType: null; }; } }; }; possibleTypes: 'BeneficiaryDemographicsData' | 'BeneficiaryReachData' | 'CreatedBoxesData' | 'MovedBoxesData' | 'StockOverviewData' | 'TopProductsCheckedOutData' | 'TopProductsDonatedData'; }; 'Date': unknown; 'Datetime': unknown; 'DeleteBoxesResult': { kind: 'UNION'; name: 'DeleteBoxesResult'; fields: {}; possibleTypes: 'BoxesResult' | 'InsufficientPermissionError'; }; @@ -52,7 +57,7 @@ export type introspection_types = { 'DeletedLocationError': { kind: 'OBJECT'; name: 'DeletedLocationError'; fields: { 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'DeletedTagError': { kind: 'OBJECT'; name: 'DeletedTagError'; fields: { 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'DimensionInfo': { kind: 'OBJECT'; name: 'DimensionInfo'; fields: { 'id': { name: 'id'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; }; - 'Dimensions': { kind: 'UNION'; name: 'Dimensions'; fields: {}; possibleTypes: 'BeneficiaryDemographicsDimensions' | 'CreatedBoxDataDimensions' | 'MovedBoxDataDimensions' | 'StockOverviewDataDimensions' | 'TopProductsDimensions'; }; + 'Dimensions': { kind: 'UNION'; name: 'Dimensions'; fields: {}; possibleTypes: 'BeneficiaryDemographicsDimensions' | 'BeneficiaryReachDimensions' | 'CreatedBoxDataDimensions' | 'MovedBoxDataDimensions' | 'StockOverviewDataDimensions' | 'TopProductsDimensions'; }; 'DisableStandardProductResult': { kind: 'UNION'; name: 'DisableStandardProductResult'; fields: {}; possibleTypes: 'BoxesStillAssignedToProductError' | 'InsufficientPermissionError' | 'Product' | 'ProductTypeMismatchError' | 'ResourceDoesNotExistError' | 'UnauthorizedForBaseError'; }; 'DistributionEvent': { kind: 'OBJECT'; name: 'DistributionEvent'; fields: { 'boxes': { name: 'boxes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Box'; ofType: null; }; }; }; } }; 'distributionEventsTrackingGroup': { name: 'distributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingGroup'; ofType: null; } }; 'distributionSpot': { name: 'distributionSpot'; type: { kind: 'OBJECT'; name: 'DistributionSpot'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'packingListEntries': { name: 'packingListEntries'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PackingListEntry'; ofType: null; }; }; }; } }; 'plannedEndDateTime': { name: 'plannedEndDateTime'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; }; } }; 'plannedStartDateTime': { name: 'plannedStartDateTime'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; }; } }; 'state': { name: 'state'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'DistributionEventState'; ofType: null; }; } }; 'unboxedItemsCollections': { name: 'unboxedItemsCollections'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UnboxedItemsCollection'; ofType: null; }; }; }; } }; }; }; 'DistributionEventCreationInput': { kind: 'INPUT_OBJECT'; name: 'DistributionEventCreationInput'; isOneOf: false; inputFields: [{ name: 'distributionSpotId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'plannedStartDateTime'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; }; }; defaultValue: null }, { name: 'plannedEndDateTime'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; }; defaultValue: null }]; }; @@ -115,11 +120,11 @@ export type introspection_types = { 'ProductsResult': { kind: 'OBJECT'; name: 'ProductsResult'; fields: { 'instantiations': { name: 'instantiations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Product'; ofType: null; }; }; }; } }; 'invalidStandardProductIds': { name: 'invalidStandardProductIds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; }; } }; }; }; 'QrCode': { kind: 'OBJECT'; name: 'QrCode'; fields: { 'box': { name: 'box'; type: { kind: 'UNION'; name: 'BoxResult'; ofType: null; } }; 'code': { name: 'code'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'createdOn': { name: 'createdOn'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; 'QrCodeResult': { kind: 'UNION'; name: 'QrCodeResult'; fields: {}; possibleTypes: 'InsufficientPermissionError' | 'QrCode' | 'ResourceDoesNotExistError'; }; - 'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'base': { name: 'base'; type: { kind: 'OBJECT'; name: 'Base'; ofType: null; } }; 'bases': { name: 'bases'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Base'; ofType: null; }; }; }; } }; 'beneficiaries': { name: 'beneficiaries'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BeneficiaryPage'; ofType: null; }; } }; 'beneficiary': { name: 'beneficiary'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'beneficiaryDemographics': { name: 'beneficiaryDemographics'; type: { kind: 'OBJECT'; name: 'BeneficiaryDemographicsData'; ofType: null; } }; 'box': { name: 'box'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'boxes': { name: 'boxes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BoxPage'; ofType: null; }; } }; 'createdBoxes': { name: 'createdBoxes'; type: { kind: 'OBJECT'; name: 'CreatedBoxesData'; ofType: null; } }; 'distributionEvent': { name: 'distributionEvent'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'distributionEventsTrackingGroup': { name: 'distributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingGroup'; ofType: null; } }; 'distributionSpot': { name: 'distributionSpot'; type: { kind: 'OBJECT'; name: 'DistributionSpot'; ofType: null; } }; 'distributionSpots': { name: 'distributionSpots'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DistributionSpot'; ofType: null; }; }; }; } }; 'location': { name: 'location'; type: { kind: 'OBJECT'; name: 'ClassicLocation'; ofType: null; } }; 'locations': { name: 'locations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ClassicLocation'; ofType: null; }; }; }; } }; 'metrics': { name: 'metrics'; type: { kind: 'OBJECT'; name: 'Metrics'; ofType: null; } }; 'movedBoxes': { name: 'movedBoxes'; type: { kind: 'OBJECT'; name: 'MovedBoxesData'; ofType: null; } }; 'organisation': { name: 'organisation'; type: { kind: 'OBJECT'; name: 'Organisation'; ofType: null; } }; 'organisations': { name: 'organisations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Organisation'; ofType: null; }; }; }; } }; 'packingListEntry': { name: 'packingListEntry'; type: { kind: 'OBJECT'; name: 'PackingListEntry'; ofType: null; } }; 'product': { name: 'product'; type: { kind: 'OBJECT'; name: 'Product'; ofType: null; } }; 'productCategories': { name: 'productCategories'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ProductCategory'; ofType: null; }; }; }; } }; 'productCategory': { name: 'productCategory'; type: { kind: 'OBJECT'; name: 'ProductCategory'; ofType: null; } }; 'products': { name: 'products'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ProductPage'; ofType: null; }; } }; 'qrCode': { name: 'qrCode'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QrCodeResult'; ofType: null; }; } }; 'qrExists': { name: 'qrExists'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'shipment': { name: 'shipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'shipments': { name: 'shipments'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; }; }; }; } }; 'sizeRanges': { name: 'sizeRanges'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SizeRange'; ofType: null; }; }; }; } }; 'standardProduct': { name: 'standardProduct'; type: { kind: 'UNION'; name: 'StandardProductResult'; ofType: null; } }; 'standardProducts': { name: 'standardProducts'; type: { kind: 'UNION'; name: 'StandardProductsResult'; ofType: null; } }; 'stockOverview': { name: 'stockOverview'; type: { kind: 'OBJECT'; name: 'StockOverviewData'; ofType: null; } }; 'tag': { name: 'tag'; type: { kind: 'OBJECT'; name: 'Tag'; ofType: null; } }; 'tags': { name: 'tags'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Tag'; ofType: null; }; }; }; } }; 'topProductsCheckedOut': { name: 'topProductsCheckedOut'; type: { kind: 'OBJECT'; name: 'TopProductsCheckedOutData'; ofType: null; } }; 'topProductsDonated': { name: 'topProductsDonated'; type: { kind: 'OBJECT'; name: 'TopProductsDonatedData'; ofType: null; } }; 'transferAgreement': { name: 'transferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'transferAgreements': { name: 'transferAgreements'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; }; }; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'users': { name: 'users'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'User'; ofType: null; }; }; }; } }; }; }; + 'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'base': { name: 'base'; type: { kind: 'OBJECT'; name: 'Base'; ofType: null; } }; 'bases': { name: 'bases'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Base'; ofType: null; }; }; }; } }; 'beneficiaries': { name: 'beneficiaries'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BeneficiaryPage'; ofType: null; }; } }; 'beneficiary': { name: 'beneficiary'; type: { kind: 'OBJECT'; name: 'Beneficiary'; ofType: null; } }; 'beneficiaryDemographics': { name: 'beneficiaryDemographics'; type: { kind: 'OBJECT'; name: 'BeneficiaryDemographicsData'; ofType: null; } }; 'beneficiaryReach': { name: 'beneficiaryReach'; type: { kind: 'OBJECT'; name: 'BeneficiaryReachData'; ofType: null; } }; 'box': { name: 'box'; type: { kind: 'OBJECT'; name: 'Box'; ofType: null; } }; 'boxes': { name: 'boxes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BoxPage'; ofType: null; }; } }; 'createdBoxes': { name: 'createdBoxes'; type: { kind: 'OBJECT'; name: 'CreatedBoxesData'; ofType: null; } }; 'distributionEvent': { name: 'distributionEvent'; type: { kind: 'OBJECT'; name: 'DistributionEvent'; ofType: null; } }; 'distributionEventsTrackingGroup': { name: 'distributionEventsTrackingGroup'; type: { kind: 'OBJECT'; name: 'DistributionEventsTrackingGroup'; ofType: null; } }; 'distributionSpot': { name: 'distributionSpot'; type: { kind: 'OBJECT'; name: 'DistributionSpot'; ofType: null; } }; 'distributionSpots': { name: 'distributionSpots'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'DistributionSpot'; ofType: null; }; }; }; } }; 'location': { name: 'location'; type: { kind: 'OBJECT'; name: 'ClassicLocation'; ofType: null; } }; 'locations': { name: 'locations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ClassicLocation'; ofType: null; }; }; }; } }; 'metrics': { name: 'metrics'; type: { kind: 'OBJECT'; name: 'Metrics'; ofType: null; } }; 'movedBoxes': { name: 'movedBoxes'; type: { kind: 'OBJECT'; name: 'MovedBoxesData'; ofType: null; } }; 'organisation': { name: 'organisation'; type: { kind: 'OBJECT'; name: 'Organisation'; ofType: null; } }; 'organisations': { name: 'organisations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Organisation'; ofType: null; }; }; }; } }; 'packingListEntry': { name: 'packingListEntry'; type: { kind: 'OBJECT'; name: 'PackingListEntry'; ofType: null; } }; 'product': { name: 'product'; type: { kind: 'OBJECT'; name: 'Product'; ofType: null; } }; 'productCategories': { name: 'productCategories'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ProductCategory'; ofType: null; }; }; }; } }; 'productCategory': { name: 'productCategory'; type: { kind: 'OBJECT'; name: 'ProductCategory'; ofType: null; } }; 'products': { name: 'products'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ProductPage'; ofType: null; }; } }; 'qrCode': { name: 'qrCode'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QrCodeResult'; ofType: null; }; } }; 'qrExists': { name: 'qrExists'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'shipment': { name: 'shipment'; type: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; } }; 'shipments': { name: 'shipments'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Shipment'; ofType: null; }; }; }; } }; 'sizeRanges': { name: 'sizeRanges'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SizeRange'; ofType: null; }; }; }; } }; 'standardProduct': { name: 'standardProduct'; type: { kind: 'UNION'; name: 'StandardProductResult'; ofType: null; } }; 'standardProducts': { name: 'standardProducts'; type: { kind: 'UNION'; name: 'StandardProductsResult'; ofType: null; } }; 'stockOverview': { name: 'stockOverview'; type: { kind: 'OBJECT'; name: 'StockOverviewData'; ofType: null; } }; 'tag': { name: 'tag'; type: { kind: 'OBJECT'; name: 'Tag'; ofType: null; } }; 'tags': { name: 'tags'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Tag'; ofType: null; }; }; }; } }; 'topProductsCheckedOut': { name: 'topProductsCheckedOut'; type: { kind: 'OBJECT'; name: 'TopProductsCheckedOutData'; ofType: null; } }; 'topProductsDonated': { name: 'topProductsDonated'; type: { kind: 'OBJECT'; name: 'TopProductsDonatedData'; ofType: null; } }; 'transferAgreement': { name: 'transferAgreement'; type: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; } }; 'transferAgreements': { name: 'transferAgreements'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TransferAgreement'; ofType: null; }; }; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'users': { name: 'users'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'User'; ofType: null; }; }; }; } }; }; }; 'ResolvedLink': { kind: 'OBJECT'; name: 'ResolvedLink'; fields: { 'baseName': { name: 'baseName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'code': { name: 'code'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'data': { name: 'data'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'DataCube'; ofType: null; }; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'organisationName': { name: 'organisationName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'urlParameters': { name: 'urlParameters'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'validUntil': { name: 'validUntil'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'view': { name: 'view'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'ShareableView'; ofType: null; }; } }; }; }; 'ResolvedLinkResult': { kind: 'UNION'; name: 'ResolvedLinkResult'; fields: {}; possibleTypes: 'ExpiredLinkError' | 'ResolvedLink' | 'UnknownLinkError'; }; 'ResourceDoesNotExistError': { kind: 'OBJECT'; name: 'ResourceDoesNotExistError'; fields: { 'id': { name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; - 'Result': { kind: 'UNION'; name: 'Result'; fields: {}; possibleTypes: 'BeneficiaryDemographicsResult' | 'CreatedBoxesResult' | 'MovedBoxesResult' | 'StockOverviewResult' | 'TopProductsCheckedOutResult' | 'TopProductsDonatedResult'; }; + 'Result': { kind: 'UNION'; name: 'Result'; fields: {}; possibleTypes: 'BeneficiaryDemographicsResult' | 'BeneficiaryReachResult' | 'CreatedBoxesResult' | 'MovedBoxesResult' | 'StockOverviewResult' | 'TopProductsCheckedOutResult' | 'TopProductsDonatedResult'; }; 'ShareableLink': { kind: 'OBJECT'; name: 'ShareableLink'; fields: { 'base': { name: 'base'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Base'; ofType: null; }; } }; 'code': { name: 'code'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'createdBy': { name: 'createdBy'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'User'; ofType: null; }; } }; 'createdOn': { name: 'createdOn'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'urlParameters': { name: 'urlParameters'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'validUntil': { name: 'validUntil'; type: { kind: 'SCALAR'; name: 'Datetime'; ofType: null; } }; 'view': { name: 'view'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'ShareableView'; ofType: null; }; } }; }; }; 'ShareableLinkCreationResult': { kind: 'UNION'; name: 'ShareableLinkCreationResult'; fields: {}; possibleTypes: 'InsufficientPermissionError' | 'InvalidDateError' | 'ShareableLink' | 'UnauthorizedForBaseError'; }; 'ShareableView': { name: 'ShareableView'; enumValues: 'StatvizDashboard' | 'StockOverview'; }; diff --git a/graphql/generated/schema.graphql b/graphql/generated/schema.graphql index c314044dd..b192cf4a1 100644 --- a/graphql/generated/schema.graphql +++ b/graphql/generated/schema.graphql @@ -126,6 +126,13 @@ enum ShareableView { StockOverview } +enum BeneficiaryReachType { + CreatedOrEdited + TagApplied + ServiceUsed + Checkout +} + """ TODO: Add description here once specs are final/confirmed """ @@ -162,8 +169,8 @@ interface DataCube { dimensions: Dimensions } -union Result = BeneficiaryDemographicsResult | CreatedBoxesResult | TopProductsCheckedOutResult | TopProductsDonatedResult | MovedBoxesResult | StockOverviewResult -union Dimensions = BeneficiaryDemographicsDimensions | CreatedBoxDataDimensions | TopProductsDimensions | MovedBoxDataDimensions | StockOverviewDataDimensions +union Result = BeneficiaryDemographicsResult | CreatedBoxesResult | TopProductsCheckedOutResult | TopProductsDonatedResult | MovedBoxesResult | StockOverviewResult | BeneficiaryReachResult +union Dimensions = BeneficiaryDemographicsDimensions | CreatedBoxDataDimensions | TopProductsDimensions | MovedBoxDataDimensions | StockOverviewDataDimensions | BeneficiaryReachDimensions type BeneficiaryDemographicsData implements DataCube { facts: [BeneficiaryDemographicsResult] @@ -183,6 +190,30 @@ type BeneficiaryDemographicsResult { count: Int } +type BeneficiaryReachData implements DataCube { + facts: [BeneficiaryReachResult!]! + dimensions: BeneficiaryReachDimensions! +} + +type BeneficiaryReachResult { + reachedOn: Date! + beneficiaryId: Int! + reachType: BeneficiaryReachType + count: Int! +} + +type BeneficiaryReachDimensions { + beneficiary: [BeneficiaryDimensionInfo!] + tag: [TagDimensionInfo!] +} + +type BeneficiaryDimensionInfo { + id: Int! + age: Int + tagIds: [Int!] + gender: HumanGender! +} + type CreatedBoxesData implements DataCube { facts: [CreatedBoxesResult] dimensions: CreatedBoxDataDimensions @@ -800,6 +831,7 @@ type Query { metrics(organisationId: ID): Metrics beneficiaryDemographics(baseId: Int!): BeneficiaryDemographicsData + beneficiaryReach(baseId: Int!): BeneficiaryReachData createdBoxes(baseId: Int!): CreatedBoxesData topProductsCheckedOut(baseId: Int!): TopProductsCheckedOutData topProductsDonated(baseId: Int!): TopProductsDonatedData