diff --git a/bbs/api/types/shop.py b/bbs/api/types/shop.py index 2561c11..13c7915 100644 --- a/bbs/api/types/shop.py +++ b/bbs/api/types/shop.py @@ -36,7 +36,7 @@ class OrderOrdering: @strawberry_django.type(models.Order) class Order: id: auto - customer: Annotated["User", strawberry.lazy(".users")] + customer: Annotated["User", strawberry.lazy(".users")] | None order_sum: auto created: auto orderlines: list["OrderLine"] diff --git a/bbs/api/types/users.py b/bbs/api/types/users.py index 6a8baff..29334ae 100644 --- a/bbs/api/types/users.py +++ b/bbs/api/types/users.py @@ -14,7 +14,7 @@ from bbs.api.types.shop import Point, ProductOrderStat class TopMonth: period: str count: int - sum: int + sum: float @strawberry_django.type(models.UserProfile) @@ -27,7 +27,10 @@ class UserProfile: @strawberry.field() def top_months(self) -> list[TopMonth]: - return [TopMonth(**month) for month in self.top_months()] + return [ + TopMonth(period=month["period"], count=month["count"], sum=float(month["sum"])) + for month in self.top_months() + ] @strawberry.field() def product_order_stats(self) -> list[ProductOrderStat]: @@ -45,4 +48,4 @@ class User: id: auto first_name: auto last_name: auto - profile: UserProfile + profile: UserProfile | None diff --git a/bbs/tests/test_seed_demo_data.py b/bbs/tests/test_seed_demo_data.py index 3eae2bd..fcba2c0 100644 --- a/bbs/tests/test_seed_demo_data.py +++ b/bbs/tests/test_seed_demo_data.py @@ -40,3 +40,26 @@ def test_demo_data_is_available_when_an_admin_has_no_profile(): assert result.data assert len(result.data["allProducts"]) == 4 assert len(result.data["allUsers"]) == 3 + + ada = user_model.objects.get(username="demo-ada") + detail_result = gql_client.query( + """ + query UserDetail($userId: ID!) { + user(userId: $userId) { + firstName + profile { + balance + lastPurchaseDate + orderSumTotal + topMonths { period sum count } + productOrderStats { productName data { x y } } + } + } + } + """, + variables={"userId": str(ada.pk)}, + ) + + assert not detail_result.errors + assert detail_result.data["user"]["profile"]["topMonths"] + assert isinstance(detail_result.data["user"]["profile"]["topMonths"][0]["sum"], float) diff --git a/frontend/codegen.yml b/frontend/codegen.yml index 170537b..382fdda 100644 --- a/frontend/codegen.yml +++ b/frontend/codegen.yml @@ -9,6 +9,9 @@ generates: DateTime: input: string output: string + Decimal: + input: string + output: string JSONScalar: input: unknown output: "Array<{ name: string; count: number }>" @@ -18,6 +21,9 @@ generates: DateTime: input: string output: string + Decimal: + input: string + output: string JSONScalar: input: unknown output: "Array<{ name: string; count: number }>" diff --git a/frontend/src/features/log/orderList.graphql b/frontend/src/features/log/orderList.graphql index 3720b22..0422f9d 100644 --- a/frontend/src/features/log/orderList.graphql +++ b/frontend/src/features/log/orderList.graphql @@ -1,5 +1,5 @@ query orderList($limit: Int = 10, $offset: Int = 0) { - orderList(order: {created: DESC}, pagination: {limit: $limit, offset: $offset}) { + orderList(ordering: [{created: DESC}], pagination: {limit: $limit, offset: $offset}) { id isExternal created diff --git a/frontend/src/features/log/transactionList.graphql b/frontend/src/features/log/transactionList.graphql index bbda04b..a098e97 100644 --- a/frontend/src/features/log/transactionList.graphql +++ b/frontend/src/features/log/transactionList.graphql @@ -1,5 +1,5 @@ query transactionList($limit: Int = 10, $offset: Int = 0) { - transactionList(order: {created: DESC}, pagination: {limit: $limit, offset: $offset}) { + transactionList(ordering: [{created: DESC}], pagination: {limit: $limit, offset: $offset}) { id created amount diff --git a/frontend/src/features/register/useRegister.ts b/frontend/src/features/register/useRegister.ts index 27ea4e6..a1679fb 100644 --- a/frontend/src/features/register/useRegister.ts +++ b/frontend/src/features/register/useRegister.ts @@ -12,7 +12,7 @@ const sumTotal = (products: Omit[], order: OrderLin const prices = order.map((orderLine: OrderLineInput) => { const product = products.find((product: Omit) => product.id === orderLine.productId); if (!product) return 0; - return orderLine.amount * (isExternal ? product.salePriceExt : product.salePriceInt); + return orderLine.amount * Number(isExternal ? product.salePriceExt : product.salePriceInt); }); return prices.reduce((partialSum, a) => partialSum + a, 0); }; diff --git a/frontend/src/features/users/UserDetail.tsx b/frontend/src/features/users/UserDetail.tsx index 2fae38f..9bdf6db 100644 --- a/frontend/src/features/users/UserDetail.tsx +++ b/frontend/src/features/users/UserDetail.tsx @@ -35,13 +35,13 @@ const UserDetail = () => {
Stats
diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 5bd0016..3d68838 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -14,6 +14,8 @@ export type Scalars = { Float: { input: number; output: number; } /** Date with time (isoformat) */ DateTime: { input: string; output: string; } + /** Decimal (fixed-point) */ + Decimal: { input: string; output: string; } /** The GenericScalar scalar type represents a generic GraphQL scalar value that could be: List or Object. */ JSONScalar: { input: unknown; output: Array<{ name: string; count: number }>; } }; @@ -93,16 +95,16 @@ export type SchemaMutationCreateUserArgs = { export type SchemaOffsetPaginationInput = { offset?: Scalars['Int']['input']; - limit?: Scalars['Int']['input']; + limit?: InputMaybe; }; export type SchemaOrder = { __typename?: 'Order'; id: Scalars['ID']['output']; customer?: Maybe; - orderSum: Scalars['Float']['output']; + orderSum: Scalars['Decimal']['output']; created: Scalars['DateTime']['output']; - orderlines?: Maybe>; + orderlines: Array; isExternal: Scalars['Boolean']['output']; }; @@ -112,7 +114,7 @@ export type SchemaOrderLine = { order: SchemaOrder; product: SchemaProduct; amount: Scalars['Int']['output']; - unitPrice: Scalars['Float']['output']; + unitPrice: Scalars['Decimal']['output']; }; export type SchemaOrderLineInput = { @@ -120,9 +122,8 @@ export type SchemaOrderLineInput = { amount: Scalars['Int']['input']; }; -export type SchemaOrderOrdering = { - created?: InputMaybe; -}; +export type SchemaOrderOrdering = + { created: SchemaOrdering; }; export type SchemaOrderStats = { __typename?: 'OrderStats'; @@ -140,7 +141,11 @@ export type SchemaOrderStatsByTime = { export enum SchemaOrdering { Asc = 'ASC', - Desc = 'DESC' + AscNullsFirst = 'ASC_NULLS_FIRST', + AscNullsLast = 'ASC_NULLS_LAST', + Desc = 'DESC', + DescNullsFirst = 'DESC_NULLS_FIRST', + DescNullsLast = 'DESC_NULLS_LAST' } export type SchemaPoint = { @@ -154,14 +159,18 @@ export type SchemaProduct = { id: Scalars['ID']['output']; name: Scalars['String']['output']; imageUrl: Scalars['String']['output']; - salePriceInt: Scalars['Float']['output']; - salePriceExt: Scalars['Float']['output']; + salePriceInt: Scalars['Decimal']['output']; + salePriceExt: Scalars['Decimal']['output']; userCounts: Scalars['JSONScalar']['output']; }; export type SchemaProductFilter = { id?: InputMaybe; active?: InputMaybe; + AND?: InputMaybe; + OR?: InputMaybe; + NOT?: InputMaybe; + DISTINCT?: InputMaybe; }; export type SchemaProductOrderStat = { @@ -184,8 +193,8 @@ export type SchemaQuery = { export type SchemaQueryTransactionListArgs = { + ordering?: Array; pagination?: InputMaybe; - order?: InputMaybe; }; @@ -195,8 +204,8 @@ export type SchemaQueryAllProductsArgs = { export type SchemaQueryOrderListArgs = { + ordering?: Array; pagination?: InputMaybe; - order?: InputMaybe; }; @@ -208,20 +217,19 @@ export type SchemaTopMonth = { __typename?: 'TopMonth'; period: Scalars['String']['output']; count: Scalars['Int']['output']; - sum: Scalars['Int']['output']; + sum: Scalars['Float']['output']; }; export type SchemaTransaction = { __typename?: 'Transaction'; id: Scalars['ID']['output']; user: SchemaUser; - amount: Scalars['Float']['output']; + amount: Scalars['Decimal']['output']; created: Scalars['DateTime']['output']; }; -export type SchemaTransactionOrdering = { - created?: InputMaybe; -}; +export type SchemaTransactionOrdering = + { created: SchemaOrdering; }; export type SchemaUser = { __typename?: 'User'; @@ -240,12 +248,12 @@ export type SchemaUserInput = { export type SchemaUserProfile = { __typename?: 'UserProfile'; id: Scalars['ID']['output']; - balance: Scalars['Float']['output']; + balance: Scalars['Decimal']['output']; image: Scalars['String']['output']; lastPurchaseDate: Scalars['DateTime']['output']; - orderSumTotal: Scalars['Float']['output']; - productOrderStats: Array; + orderSumTotal: Scalars['Decimal']['output']; topMonths: Array; + productOrderStats: Array; }; export type OrderLineInput = { @@ -269,7 +277,7 @@ export type CreateDepositMutationVariables = Exact<{ export type CreateDepositMutation = { __typename: 'Mutation', createDeposit: - | { __typename: 'CreateDepositSuccess', transaction: { __typename: 'Transaction', id: string, amount: number, user: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: number } | null } } } + | { __typename: 'CreateDepositSuccess', transaction: { __typename: 'Transaction', id: string, amount: string, user: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: string } | null } } } | { __typename: 'AboveMaxSaldo', error: boolean, message: string, maxSaldo: number, above: number } | { __typename: 'FormErrors', error: boolean, message: string, fields: Array<{ __typename: 'FieldError', field: string, message: string }> } }; @@ -280,7 +288,7 @@ export type OrderListQueryVariables = Exact<{ }>; -export type OrderListQuery = { __typename: 'Query', orderList: Array<{ __typename: 'Order', id: string, isExternal: boolean, created: string, orderSum: number, customer: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: number } | null } | null, orderlines: Array<{ __typename: 'OrderLine', id: string, amount: number, unitPrice: number, product: { __typename: 'Product', id: string, name: string, salePriceExt: number, salePriceInt: number } }> | null }> }; +export type OrderListQuery = { __typename: 'Query', orderList: Array<{ __typename: 'Order', id: string, isExternal: boolean, created: string, orderSum: string, customer: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: string } | null } | null, orderlines: Array<{ __typename: 'OrderLine', id: string, amount: number, unitPrice: string, product: { __typename: 'Product', id: string, name: string, salePriceExt: string, salePriceInt: string } }> }> }; export type TransactionListQueryVariables = Exact<{ limit?: number | null | undefined; @@ -288,19 +296,19 @@ export type TransactionListQueryVariables = Exact<{ }>; -export type TransactionListQuery = { __typename: 'Query', transactionList: Array<{ __typename: 'Transaction', id: string, created: string, amount: number, user: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: number } | null } }> }; +export type TransactionListQuery = { __typename: 'Query', transactionList: Array<{ __typename: 'Transaction', id: string, created: string, amount: string, user: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: string } | null } }> }; export type AllProductsQueryVariables = Exact<{ active: boolean; }>; -export type AllProductsQuery = { __typename: 'Query', allProducts: Array<{ __typename: 'Product', id: string, name: string, imageUrl: string, salePriceExt: number, salePriceInt: number }> }; +export type AllProductsQuery = { __typename: 'Query', allProducts: Array<{ __typename: 'Product', id: string, name: string, imageUrl: string, salePriceExt: string, salePriceInt: string }> }; export type AllUsersQueryVariables = Exact<{ [key: string]: never; }>; -export type AllUsersQuery = { __typename: 'Query', allUsers: Array<{ __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: number, image: string, lastPurchaseDate: string } | null }> }; +export type AllUsersQuery = { __typename: 'Query', allUsers: Array<{ __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: string, image: string, lastPurchaseDate: string } | null }> }; export type CreateOrderMutationVariables = Exact<{ customerId: string | number; @@ -310,7 +318,7 @@ export type CreateOrderMutationVariables = Exact<{ export type CreateOrderMutation = { __typename: 'Mutation', createOrder: - | { __typename: 'CreateOrderSuccess', order: { __typename: 'Order', id: string, isExternal: boolean, orderSum: number, customer: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: number } | null } | null, orderlines: Array<{ __typename: 'OrderLine', id: string, amount: number, unitPrice: number, product: { __typename: 'Product', id: string, name: string, salePriceExt: number, salePriceInt: number } }> | null } } + | { __typename: 'CreateOrderSuccess', order: { __typename: 'Order', id: string, isExternal: boolean, orderSum: string, customer: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: string } | null } | null, orderlines: Array<{ __typename: 'OrderLine', id: string, amount: number, unitPrice: string, product: { __typename: 'Product', id: string, name: string, salePriceExt: string, salePriceInt: string } }> } } | { __typename: 'InsufficientFunds', error: boolean, message: string, amountLacking: number } | { __typename: 'FormErrors', error: boolean, message: string, fields: Array<{ __typename: 'FieldError', field: string, message: string }> } }; @@ -337,13 +345,13 @@ export type UserDetailQueryVariables = Exact<{ }>; -export type UserDetailQuery = { __typename: 'Query', user: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: number, image: string, lastPurchaseDate: string, orderSumTotal: number, topMonths: Array<{ __typename: 'TopMonth', period: string, sum: number, count: number }>, productOrderStats: Array<{ __typename: 'ProductOrderStat', productName: string, data: Array<{ __typename: 'Point', x: string, y: number }> }> } | null } }; +export type UserDetailQuery = { __typename: 'Query', user: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string, balance: string, image: string, lastPurchaseDate: string, orderSumTotal: string, topMonths: Array<{ __typename: 'TopMonth', period: string, sum: number, count: number }>, productOrderStats: Array<{ __typename: 'ProductOrderStat', productName: string, data: Array<{ __typename: 'Point', x: string, y: number }> }> } | null } }; export const CreateUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"firstName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lastName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"firstName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"firstName"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"lastName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lastName"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateDepositDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateDeposit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"amount"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createDeposit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"userId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userId"}}},{"kind":"Argument","name":{"kind":"Name","value":"amount"},"value":{"kind":"Variable","name":{"kind":"Name","value":"amount"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CreateDepositSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"transaction"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AboveMaxSaldo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"maxSaldo"}},{"kind":"Field","name":{"kind":"Name","value":"above"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FormErrors"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const OrderListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"orderList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"10"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orderList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"order"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"created"},"value":{"kind":"EnumValue","value":"DESC"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isExternal"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"orderSum"}},{"kind":"Field","name":{"kind":"Name","value":"customer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderlines"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"unitPrice"}},{"kind":"Field","name":{"kind":"Name","value":"product"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceInt"}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const TransactionListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"transactionList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"10"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"transactionList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"order"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"created"},"value":{"kind":"EnumValue","value":"DESC"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const OrderListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"orderList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"10"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orderList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ordering"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"created"},"value":{"kind":"EnumValue","value":"DESC"}}]}]}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isExternal"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"orderSum"}},{"kind":"Field","name":{"kind":"Name","value":"customer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderlines"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"unitPrice"}},{"kind":"Field","name":{"kind":"Name","value":"product"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceInt"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const TransactionListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"transactionList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"10"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"transactionList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ordering"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"created"},"value":{"kind":"EnumValue","value":"DESC"}}]}]}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const AllProductsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allProducts"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"active"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allProducts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"Variable","name":{"kind":"Name","value":"active"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceInt"}}]}}]}}]} as unknown as DocumentNode; export const AllUsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allUsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allUsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"lastPurchaseDate"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"customerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderLines"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"OrderLineInput"}}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"isExternal"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOrder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"customerId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"customerId"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderLines"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderLines"}}},{"kind":"Argument","name":{"kind":"Name","value":"isExternal"},"value":{"kind":"Variable","name":{"kind":"Name","value":"isExternal"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CreateOrderSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isExternal"}},{"kind":"Field","name":{"kind":"Name","value":"customer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"profile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"balance"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderlines"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"unitPrice"}},{"kind":"Field","name":{"kind":"Name","value":"product"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceInt"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"orderSum"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"InsufficientFunds"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"amountLacking"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FormErrors"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/frontend/src/utils/users.ts b/frontend/src/utils/users.ts index bc68183..392af4c 100644 --- a/frontend/src/utils/users.ts +++ b/frontend/src/utils/users.ts @@ -7,7 +7,7 @@ const activeLimitDays = 365 / 2; type FilterableUser = AllUsersQuery["allUsers"][0]; export const activeUserFilter = (user: FilterableUser) => { - const balance = user.profile?.balance || 0; + const balance = Number(user.profile?.balance) || 0; const active = !!user.profile?.lastPurchaseDate && parseISO(user.profile.lastPurchaseDate) >= subDays(new Date(), activeLimitDays); return balance >= 15 && active; diff --git a/schema.graphql b/schema.graphql index 10e1672..0efe81f 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1,3 +1,5 @@ +directive @oneOf on INPUT_OBJECT + type AboveMaxSaldo implements Error { message: String! error: Boolean! @@ -20,6 +22,9 @@ type CreateOrderSuccess { """Date with time (isoformat)""" scalar DateTime +"""Decimal (fixed-point)""" +scalar Decimal + interface Error { message: String! error: Boolean! @@ -56,15 +61,15 @@ type Mutation { input OffsetPaginationInput { offset: Int! = 0 - limit: Int! = -1 + limit: Int } type Order { id: ID! customer: User - orderSum: Float! + orderSum: Decimal! created: DateTime! - orderlines: [OrderLine!] + orderlines: [OrderLine!]! isExternal: Boolean! } @@ -73,7 +78,7 @@ type OrderLine { order: Order! product: Product! amount: Int! - unitPrice: Float! + unitPrice: Decimal! } input OrderLineInput { @@ -81,7 +86,7 @@ input OrderLineInput { amount: Int! } -input OrderOrdering { +input OrderOrdering @oneOf { created: Ordering } @@ -99,7 +104,11 @@ type OrderStatsByTime { enum Ordering { ASC + ASC_NULLS_FIRST + ASC_NULLS_LAST DESC + DESC_NULLS_FIRST + DESC_NULLS_LAST } type Point { @@ -111,14 +120,18 @@ type Product { id: ID! name: String! imageUrl: String! - salePriceInt: Float! - salePriceExt: Float! + salePriceInt: Decimal! + salePriceExt: Decimal! userCounts: JSONScalar! } input ProductFilter { id: ID active: Boolean + AND: ProductFilter + OR: ProductFilter + NOT: ProductFilter + DISTINCT: Boolean } type ProductOrderStat { @@ -127,10 +140,10 @@ type ProductOrderStat { } type Query { - transactionList(pagination: OffsetPaginationInput, order: TransactionOrdering): [Transaction!]! + transactionList(ordering: [TransactionOrdering!]! = [], pagination: OffsetPaginationInput): [Transaction!]! allProducts(filters: ProductFilter): [Product!]! allOrders: [Order!]! - orderList(pagination: OffsetPaginationInput, order: OrderOrdering): [Order!]! + orderList(ordering: [OrderOrdering!]! = [], pagination: OffsetPaginationInput): [Order!]! orderStats: OrderStats! productStats: [ProductOrderStat!]! allUsers: [User!]! @@ -140,17 +153,17 @@ type Query { type TopMonth { period: String! count: Int! - sum: Int! + sum: Float! } type Transaction { id: ID! user: User! - amount: Float! + amount: Decimal! created: DateTime! } -input TransactionOrdering { +input TransactionOrdering @oneOf { created: Ordering } @@ -169,10 +182,10 @@ input UserInput { type UserProfile { id: ID! - balance: Float! + balance: Decimal! image: String! lastPurchaseDate: DateTime! - orderSumTotal: Float! - productOrderStats: [ProductOrderStat!]! + orderSumTotal: Decimal! topMonths: [TopMonth!]! + productOrderStats: [ProductOrderStat!]! } \ No newline at end of file