feat(inventory): add and edit products without the admin

The inventory page gets a "Ny vare" link and a "Rediger" link per product,
leading to a form for name, prices, volume, alcohol, description and whether
the product is for sale. Stock still changes only through inventory
transactions and sales, and the image through the admin.

Saving needs the same access as the Django admin: staff, plus the add or
change product permission (superusers have both). The server checks this on
every save and validates with the model's rules. Logged out visitors are
sent to log in and back to the form.

New in the API: createProduct and updateProduct mutations, a product query,
and a me query so the frontend knows who is logged in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leanderandClaude Opus 5.5 committed 2026-11-05 03:44:12 +01:00
1 parent 31eb71b354
commit 7ab2a0f1f2
18 files changed
+760 -4

No files matched your search

+101
View File
@@ -0,0 +1,101 @@
from decimal import Decimal
from typing import Annotated
import strawberry
from django.core.exceptions import ValidationError
from django.db import transaction
from strawberry import ID, Info
from bbs import models
from bbs.api.errors import FieldError, FormErrors
from bbs.api.permissions import can_add_products, can_change_products
from bbs.api.types.shop import Product
@strawberry.input
class ProductInput:
name: str
sale_price_int: Decimal
sale_price_ext: Decimal
volume_liter: Decimal | None = None
alcohol_percent: Decimal | None = None
description: str = ""
active: bool = True
# The stock changes through inventory transactions and sales, and the image through the admin
EDITABLE_FIELDS = [
"name",
"sale_price_int",
"sale_price_ext",
"volume_liter",
"alcohol_percent",
"description",
"active",
]
NON_NEGATIVE_FIELDS = ["sale_price_int", "sale_price_ext", "volume_liter", "alcohol_percent"]
@strawberry.type
class SaveProductSuccess:
product: Product
SaveProductResponse = Annotated[SaveProductSuccess | FormErrors, strawberry.union("SaveProductResponse")]
def _access_denied(user) -> FormErrors:
message = "Du har ikke tilgang til å endre varer." if user.is_authenticated else "Logg inn for å endre varer."
return FormErrors(message=message, fields=[FieldError(field="user", message=message)])
def _save(product: models.Product, data: ProductInput) -> SaveProductResponse:
for field in EDITABLE_FIELDS:
setattr(product, field, getattr(data, field))
errors: dict[str, list[str]] = {}
try:
product.full_clean()
except ValidationError as e:
errors = e.message_dict
for field in NON_NEGATIVE_FIELDS:
value = getattr(product, field)
if value is not None and value < 0:
errors.setdefault(field, []).append("Kan ikke være negativ.")
if errors:
return FormErrors(
message="Varen ble ikke lagret. Rett opp feilene i skjemaet.",
fields=[FieldError(field=field, message=" ".join(messages)) for field, messages in errors.items()],
)
product.save()
return SaveProductSuccess(product=product)
@strawberry.mutation
@transaction.atomic
def create_product(info: Info, data: ProductInput) -> SaveProductResponse:
user = info.context.request.user
if not can_add_products(user):
return _access_denied(user)
return _save(models.Product(), data)
@strawberry.mutation
@transaction.atomic
def update_product(info: Info, product_id: ID, data: ProductInput) -> SaveProductResponse:
user = info.context.request.user
if not can_change_products(user):
return _access_denied(user)
try:
product = models.Product.objects.select_for_update().get(pk=product_id)
except models.Product.DoesNotExist, ValueError:
message = "Fant ikke varen."
return FormErrors(message=message, fields=[FieldError(field="product_id", message=message)])
return _save(product, data)
@strawberry.type
class ProductMutations:
create_product = create_product
update_product = update_product
+14
View File
@@ -0,0 +1,14 @@
"""Who may do what through the API, using the same rules as the Django admin."""
def _admin_can(user, permission):
# The admin only lets in staff, and then checks the model permission (superusers have all of them)
return user.is_authenticated and user.is_staff and user.has_perm(permission)
def can_add_products(user):
return _admin_can(user, "bbs.add_product")
def can_change_products(user):
return _admin_can(user, "bbs.change_product")
+4
View File
@@ -19,6 +19,10 @@ class ShopQueries:
all_orders: list[Order] = strawberry_django.field()
order_list: list[Order] = strawberry_django.field(pagination=True, ordering=OrderOrdering)
@strawberry.field
def product(self, product_id: strawberry.ID) -> Product:
return models.Product.objects.get(pk=product_id)
@strawberry.field
def order_stats(self) -> OrderStats:
return OrderStats(
+15 -2
View File
@@ -1,8 +1,9 @@
import strawberry
from django.contrib.auth.models import User as DjangoUser
from strawberry import ID
from strawberry import ID, Info
from bbs.api.types.users import User
from bbs.api.permissions import can_add_products, can_change_products
from bbs.api.types.users import User, Viewer
@strawberry.type
@@ -14,3 +15,15 @@ class UsersQueries:
@strawberry.field
def user(self, user_id: ID) -> User:
return DjangoUser.objects.get(pk=user_id)
@strawberry.field(description="The logged in user, or null when nobody is logged in")
def me(self, info: Info) -> Viewer | None:
user = info.context.request.user
if not user.is_authenticated:
return None
return Viewer(
id=user.pk,
first_name=user.first_name,
can_add_products=can_add_products(user),
can_change_products=can_change_products(user),
)
+2 -1
View File
@@ -3,6 +3,7 @@ from django.core.exceptions import ObjectDoesNotExist
from strawberry.extensions import SchemaExtension
from bbs.api.mutations.inventory import InventoryMutations
from bbs.api.mutations.products import ProductMutations
from bbs.api.mutations.shop import ShopMutations
from bbs.api.mutations.users import UsersMutations
from bbs.api.queries.inventory import InventoryQueries
@@ -16,7 +17,7 @@ class Query(InventoryQueries, ShopQueries, UsersQueries):
@strawberry.type
class Mutation(InventoryMutations, ShopMutations, UsersMutations):
class Mutation(InventoryMutations, ProductMutations, ShopMutations, UsersMutations):
pass
+3
View File
@@ -22,6 +22,9 @@ class ProductFilter:
class Product:
id: auto
name: auto
description: auto
volume_liter: auto
alcohol_percent: auto
image_url: str
sale_price_int: auto
sale_price_ext: auto
+8
View File
@@ -43,6 +43,14 @@ class UserProfile:
]
@strawberry.type
class Viewer:
id: strawberry.ID
first_name: str
can_add_products: bool
can_change_products: bool
@strawberry_django.type(DjangoUser)
class User:
id: auto
+175
View File
@@ -0,0 +1,175 @@
from decimal import Decimal
import pytest
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User as DjangoUser
from bbs.models import Product
ME = "query { me { firstName canAddProducts canChangeProducts } }"
SAVE_RESULT = """
... on SaveProductSuccess {
product { id name salePriceInt salePriceExt volumeLiter alcoholPercent description active inventoryAmount }
}
... on FormErrors { message fields { field message } }
"""
CREATE_PRODUCT = f"mutation CreateProduct($data: ProductInput!) {{ createProduct(data: $data) {{ {SAVE_RESULT} }} }}"
UPDATE_PRODUCT = f"""
mutation UpdateProduct($productId: ID!, $data: ProductInput!) {{
updateProduct(productId: $productId, data: $data) {{ {SAVE_RESULT} }}
}}
"""
PRODUCT_DATA = {
"name": "Ny pils",
"salePriceInt": "25.0",
"salePriceExt": "30.0",
"volumeLiter": "0.5",
"alcoholPercent": "4.7",
"description": "Frisk",
"active": True,
}
def make_user(username, *, staff=False, superuser=False, permissions=()):
user = DjangoUser.objects.create_user(username=username, first_name=username.title())
user.is_staff = staff
user.is_superuser = superuser
user.save()
user.user_permissions.set(Permission.objects.filter(content_type__app_label="bbs", codename__in=permissions))
return user
@pytest.fixture
def staff_with_product_access(db): # noqa: ARG001
return make_user("lager", staff=True, permissions=["add_product", "change_product"])
# me
# ==
def test_me_is_null_when_logged_out(db, gql_client): # noqa: ARG001
res = gql_client.query(ME)
assert not res.errors
assert res.data["me"] is None
@pytest.mark.parametrize(
("staff", "superuser", "permissions", "can_add", "can_change"),
[
(False, False, ["add_product", "change_product"], False, False), # permissions without admin access
(True, False, [], False, False),
(True, False, ["change_product"], False, True),
(True, False, ["add_product", "change_product"], True, True),
(True, True, [], True, True),
],
)
def test_me_reports_product_access(db, gql_client, client, staff, superuser, permissions, can_add, can_change): # noqa: ARG001, PLR0913, PLR0917
client.force_login(make_user("someone", staff=staff, superuser=superuser, permissions=permissions))
res = gql_client.query(ME)
assert not res.errors
assert res.data["me"] == {"firstName": "Someone", "canAddProducts": can_add, "canChangeProducts": can_change}
# createProduct
# =============
def test_create_product(gql_client, client, staff_with_product_access):
client.force_login(staff_with_product_access)
res = gql_client.query(CREATE_PRODUCT, {"data": PRODUCT_DATA})
assert not res.errors
product = res.data["createProduct"]["product"]
assert product["name"] == "Ny pils"
assert product["inventoryAmount"] == 0
saved = Product.objects.get(pk=product["id"])
assert saved.sale_price_int == Decimal("25.0")
assert saved.volume_liter == Decimal("0.5")
assert saved.active
@pytest.mark.parametrize(
("user_kwargs", "message"),
[
(None, "Logg inn for å endre varer."),
({}, "Du har ikke tilgang til å endre varer."),
({"permissions": ["add_product"]}, "Du har ikke tilgang til å endre varer."), # not staff
({"staff": True, "permissions": ["change_product"]}, "Du har ikke tilgang til å endre varer."),
],
)
def test_create_product_requires_admin_access(db, gql_client, client, user_kwargs, message): # noqa: ARG001
if user_kwargs is not None:
client.force_login(make_user("someone", **user_kwargs))
res = gql_client.query(CREATE_PRODUCT, {"data": PRODUCT_DATA})
assert not res.errors
assert res.data["createProduct"]["message"] == message
assert not Product.objects.exists()
@pytest.mark.parametrize(
("changes", "field"),
[
({"name": ""}, "name"),
({"salePriceInt": "-1"}, "sale_price_int"),
({"salePriceExt": "12.55"}, "sale_price_ext"), # one decimal at most
({"volumeLiter": None}, "volume_liter"),
({"alcoholPercent": "-0.5"}, "alcohol_percent"),
],
)
def test_create_product_validates(gql_client, client, staff_with_product_access, changes, field):
client.force_login(staff_with_product_access)
res = gql_client.query(CREATE_PRODUCT, {"data": {**PRODUCT_DATA, **changes}})
assert not res.errors
errors = res.data["createProduct"]
assert errors["message"] == "Varen ble ikke lagret. Rett opp feilene i skjemaet."
assert [error["field"] for error in errors["fields"]] == [field]
assert not Product.objects.exists()
# updateProduct
# =============
def test_update_product(product, gql_client, client):
product.inventory_amount = 7
product.save()
client.force_login(make_user("admin", staff=True, superuser=True))
res = gql_client.query(UPDATE_PRODUCT, {"productId": str(product.id), "data": {**PRODUCT_DATA, "active": False}})
assert not res.errors
assert res.data["updateProduct"]["product"]["name"] == "Ny pils"
product.refresh_from_db()
assert product.name == "Ny pils"
assert product.sale_price_ext == Decimal("30.0")
assert not product.active
assert product.inventory_amount == 7 # stock only changes through transactions and sales
def test_update_product_requires_change_permission(product, gql_client, client):
client.force_login(make_user("someone", staff=True, permissions=["add_product"]))
res = gql_client.query(UPDATE_PRODUCT, {"productId": str(product.id), "data": PRODUCT_DATA})
assert not res.errors
assert res.data["updateProduct"]["message"] == "Du har ikke tilgang til å endre varer."
product.refresh_from_db()
assert product.name == "borg"
def test_update_missing_product(gql_client, client, staff_with_product_access):
client.force_login(staff_with_product_access)
res = gql_client.query(UPDATE_PRODUCT, {"productId": "999", "data": PRODUCT_DATA})
assert not res.errors
assert res.data["updateProduct"]["message"] == "Fant ikke varen."
# product
# =======
def test_product_query(product, gql_client):
res = gql_client.query("query P($id: ID!) { product(productId: $id) { name } }", {"id": str(product.id)})
assert not res.errors
assert res.data["product"]["name"] == "borg"
+3
View File
@@ -7,6 +7,7 @@ const Deposit = React.lazy(() => import("./features/deposit/Deposit"));
const Inventory = React.lazy(() => import("./features/inventory/Inventory"));
const InventoryAdd = React.lazy(() => import("./features/inventory/InventoryAdd"));
const InventoryReport = React.lazy(() => import("./features/inventory/InventoryReport"));
const ProductEdit = React.lazy(() => import("./features/inventory/ProductEdit"));
const Log = React.lazy(() => import("./features/log/Log"));
const Register = React.lazy(() => import("./features/register/Register"));
const OrderStats = React.lazy(() => import("./features/stats/OrderStats"));
@@ -22,6 +23,8 @@ export const Routes = (): React.JSX.Element => (
<Route path="/deposit" element={<Deposit />} />
<Route path="/inventory" element={<Inventory />} />
<Route path="/inventory/add" element={<InventoryAdd />} />
<Route path="/inventory/products/new" element={<ProductEdit />} />
<Route path="/inventory/products/:productId" element={<ProductEdit />} />
<Route path="/report" element={<InventoryReport />} />
<Route path="/log" element={<Log />} />
<Route path="/stats" element={<Stats />} />
@@ -0,0 +1,8 @@
query viewer {
me {
id
firstName
canAddProducts
canChangeProducts
}
}
@@ -50,6 +50,12 @@
min-height: 6rem;
}
.form label.checkbox {
display: flex;
align-items: center;
gap: 0.5rem;
}
.summary {
display: flex;
flex-wrap: wrap;
@@ -70,3 +76,13 @@
min-width: 42rem;
}
}
/* Read by screen readers, not shown */
.visuallyHidden {
position: absolute;
overflow: hidden;
width: 1px;
height: 1px;
clip-path: inset(50%);
white-space: nowrap;
}
+15 -1
View File
@@ -4,7 +4,7 @@ import { Link } from "react-router-dom";
import styles from "./Inventory.module.css";
import { GQLError } from "../../components/GQLError";
import { InventoryPageDocument } from "../../types";
import { InventoryPageDocument, ViewerDocument } from "../../types";
import { formatCurrency } from "../../utils/currency";
import { formatTime } from "../../utils/time";
@@ -15,6 +15,8 @@ const Inventory = () => {
const { data, loading, error } = useQuery(InventoryPageDocument, {
variables: { limit: PAGE_SIZE, offset },
});
const { data: viewerData } = useQuery(ViewerDocument);
const canChangeProducts = !!viewerData?.me?.canChangeProducts;
if (loading) return <div>Loading...</div>;
if (error) return <GQLError error={error} />;
@@ -42,6 +44,8 @@ const Inventory = () => {
<Link className="btn-primary" to="/inventory/add">
Registrer varetransaksjon
</Link>
{/* Shown to everyone; the page asks for a login, and for admin access, when needed */}
<Link to="/inventory/products/new">Ny vare</Link>
<Link to="/report">Åpne rapport</Link>
</div>
<div className={styles.tableWrap}>
@@ -54,6 +58,11 @@ const Inventory = () => {
<th scope="col">Verdi</th>
<th scope="col">I salg</th>
<th scope="col">Sist oppdatert</th>
{canChangeProducts && (
<th scope="col">
<span className={styles.visuallyHidden}>Endre</span>
</th>
)}
</tr>
</thead>
<tbody>
@@ -65,6 +74,11 @@ const Inventory = () => {
<td>{formatCurrency(Number(product.wholesaleValue))} kr</td>
<td>{product.active ? "Ja" : "Nei"}</td>
<td>{formatTime(product.updated)}</td>
{canChangeProducts && (
<td>
<Link to={`/inventory/products/${product.id}`}>Rediger</Link>
</td>
)}
</tr>
))}
</tbody>
@@ -0,0 +1,206 @@
import { useMutation, useQuery } from "@apollo/client/react";
import React, { useEffect, useState } from "react";
import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
import styles from "./Inventory.module.css";
import { GQLError } from "../../components/GQLError";
import { useToast } from "../../hooks/useToast";
import {
CreateProductDocument,
ProductFormDocument,
ProductFormQuery,
ProductInput,
UpdateProductDocument,
ViewerDocument,
} from "../../types";
import { loginUrl } from "../../utils/api";
type FormValues = {
name: string;
salePriceInt: string;
salePriceExt: string;
volumeLiter: string;
alcoholPercent: string;
description: string;
active: boolean;
};
const emptyValues: FormValues = {
name: "",
salePriceInt: "",
salePriceExt: "",
volumeLiter: "",
alcoholPercent: "",
description: "",
active: true,
};
const toFormValues = (product: ProductFormQuery["product"]): FormValues => ({
name: product.name,
salePriceInt: product.salePriceInt,
salePriceExt: product.salePriceExt,
volumeLiter: product.volumeLiter ?? "",
alcoholPercent: product.alcoholPercent ?? "",
description: product.description,
active: product.active,
});
const toInput = (values: FormValues): ProductInput => ({
...values,
volumeLiter: values.volumeLiter || null,
alcoholPercent: values.alcoholPercent || null,
});
// Keyed by the API's field names, e.g. sale_price_int
type FieldErrors = Record<string, string>;
const FieldError = ({ errors, field }: { errors: FieldErrors; field: string }) =>
errors[field] ? <span className={styles.error}>{errors[field]}</span> : null;
const ProductForm = ({ productId, initialValues }: { productId?: string; initialValues: FormValues }) => {
const navigate = useNavigate();
const { showToast } = useToast();
const [values, setValues] = useState(initialValues);
const [fieldErrors, setFieldErrors] = useState<FieldErrors>({});
const [createProduct, { loading: creating }] = useMutation(CreateProductDocument, {
// The product lists don't know about the new product; have them load again
update: (cache) => {
cache.evict({ id: "ROOT_QUERY", fieldName: "allProducts" });
cache.gc();
},
});
const [updateProduct, { loading: updating }] = useMutation(UpdateProductDocument);
const saving = creating || updating;
const set =
(field: Exclude<keyof FormValues, "active">) =>
(event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
setValues({ ...values, [field]: event.target.value });
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = toInput(values);
try {
const response = productId
? (await updateProduct({ variables: { productId, data } })).data?.updateProduct
: (await createProduct({ variables: { data } })).data?.createProduct;
if (response?.__typename === "SaveProductSuccess") {
showToast(`Lagret ${response.product.name}.`);
navigate("/inventory");
} else if (response?.__typename === "FormErrors") {
setFieldErrors(Object.fromEntries(response.fields.map(({ field, message }) => [field, message])));
showToast(response.message, "danger");
} else {
showToast("Kunne ikke lagre varen.", "danger");
}
} catch (mutationError) {
showToast(mutationError instanceof Error ? mutationError.message : "Kunne ikke lagre varen.", "danger");
}
};
return (
<form className={styles.form} onSubmit={onSubmit}>
<label>
Navn
<input required maxLength={64} value={values.name} onChange={set("name")} />
<FieldError errors={fieldErrors} field="name" />
</label>
<label>
Pris internt (kr)
<input required type="number" min="0" step="0.1" value={values.salePriceInt} onChange={set("salePriceInt")} />
<FieldError errors={fieldErrors} field="sale_price_int" />
</label>
<label>
Pris eksternt (kr)
<input required type="number" min="0" step="0.1" value={values.salePriceExt} onChange={set("salePriceExt")} />
<FieldError errors={fieldErrors} field="sale_price_ext" />
</label>
<label>
Volum (liter)
<input required type="number" min="0" step="0.1" value={values.volumeLiter} onChange={set("volumeLiter")} />
<FieldError errors={fieldErrors} field="volume_liter" />
</label>
<label>
Alkohol (%)
<input
required
type="number"
min="0"
step="0.1"
value={values.alcoholPercent}
onChange={set("alcoholPercent")}
/>
<FieldError errors={fieldErrors} field="alcohol_percent" />
</label>
<label>
Beskrivelse
<textarea maxLength={256} value={values.description} onChange={set("description")} />
<FieldError errors={fieldErrors} field="description" />
</label>
<label className={styles.checkbox}>
<input
type="checkbox"
checked={values.active}
onChange={(event) => setValues({ ...values, active: event.target.checked })}
/>
I salg
</label>
<div className={styles.summary}>
<button type="submit" className="primary" disabled={saving}>
{saving ? "Lagrer..." : "Lagre"}
</button>
<Link to="/inventory">Avbryt</Link>
</div>
</form>
);
};
// Adds a product, or edits one at /inventory/products/:productId. Needs the same access as the Django admin.
const ProductEdit = () => {
const { productId } = useParams();
const location = useLocation();
const { data: viewerData, loading: viewerLoading, error: viewerError } = useQuery(ViewerDocument);
const { data, loading, error } = useQuery(ProductFormDocument, {
variables: { productId: productId ?? "" },
skip: !productId,
});
// Logged out: log in, and Django sends the browser back here afterwards
const loggedOut = !!viewerData && !viewerData.me;
const login = loginUrl(location.pathname);
useEffect(() => {
if (loggedOut) window.location.replace(login);
}, [loggedOut, login]);
if (viewerLoading || loading) return <div>Loading...</div>;
if (viewerError || error) return <GQLError error={(viewerError || error)!} />;
const viewer = viewerData?.me;
const title = productId ? `Rediger ${data?.product.name ?? "vare"}` : "Ny vare";
const hasAccess = productId ? viewer?.canChangeProducts : viewer?.canAddProducts;
return (
<div>
<h2>{title}</h2>
{!viewer && (
<p>
Sender deg til innlogging… Skjer det ingenting, <a href={login}>logg inn her</a>.
</p>
)}
{viewer && !hasAccess && (
<p>Du har ikke tilgang til å endre varer. Be en administrator om tilgang til varer i adminpanelet.</p>
)}
{hasAccess && (
<>
<p>Beholdningen endres med varetransaksjoner, og bildet i adminpanelet.</p>
<ProductForm
key={productId ?? "new"}
productId={productId}
initialValues={data ? toFormValues(data.product) : emptyValues}
/>
</>
)}
</div>
);
};
export default ProductEdit;
@@ -0,0 +1,25 @@
mutation createProduct($data: ProductInput!) {
createProduct(data: $data) {
... on SaveProductSuccess {
product {
id
name
salePriceInt
salePriceExt
volumeLiter
alcoholPercent
description
active
inventoryAmount
updated
}
}
... on FormErrors {
message
fields {
field
message
}
}
}
}
@@ -0,0 +1,12 @@
query productForm($productId: ID!) {
product(productId: $productId) {
id
name
salePriceInt
salePriceExt
volumeLiter
alcoholPercent
description
active
}
}
@@ -0,0 +1,25 @@
mutation updateProduct($productId: ID!, $data: ProductInput!) {
updateProduct(productId: $productId, data: $data) {
... on SaveProductSuccess {
product {
id
name
salePriceInt
salePriceExt
volumeLiter
alcoholPercent
description
active
inventoryAmount
updated
}
}
... on FormErrors {
message
fields {
field
message
}
}
}
}
+96
View File
@@ -120,6 +120,8 @@ export type SchemaInventoryTransactionOrdering =
export type SchemaMutation = {
__typename?: 'Mutation';
createInventoryTransaction: SchemaCreateInventoryTransactionResponse;
createProduct: SchemaSaveProductResponse;
updateProduct: SchemaSaveProductResponse;
createOrder: SchemaCreateOrderResponse;
createDeposit: SchemaCreateDepositResponse;
createUser: SchemaUser;
@@ -131,6 +133,17 @@ export type SchemaMutationCreateInventoryTransactionArgs = {
};
export type SchemaMutationCreateProductArgs = {
data: SchemaProductInput;
};
export type SchemaMutationUpdateProductArgs = {
productId: Scalars['ID']['input'];
data: SchemaProductInput;
};
export type SchemaMutationCreateOrderArgs = {
customerId: Scalars['ID']['input'];
orderLines: Array<SchemaOrderLineInput>;
@@ -213,6 +226,9 @@ export type SchemaProduct = {
__typename?: 'Product';
id: Scalars['ID']['output'];
name: Scalars['String']['output'];
description: Scalars['String']['output'];
volumeLiter?: Maybe<Scalars['Decimal']['output']>;
alcoholPercent?: Maybe<Scalars['Decimal']['output']>;
imageUrl: Scalars['String']['output'];
salePriceInt: Scalars['Decimal']['output'];
salePriceExt: Scalars['Decimal']['output'];
@@ -233,6 +249,16 @@ export type SchemaProductFilter = {
DISTINCT?: InputMaybe<Scalars['Boolean']['input']>;
};
export type SchemaProductInput = {
name: Scalars['String']['input'];
salePriceInt: Scalars['Decimal']['input'];
salePriceExt: Scalars['Decimal']['input'];
volumeLiter?: InputMaybe<Scalars['Decimal']['input']>;
alcoholPercent?: InputMaybe<Scalars['Decimal']['input']>;
description?: Scalars['String']['input'];
active?: Scalars['Boolean']['input'];
};
export type SchemaProductOrderStat = {
__typename?: 'ProductOrderStat';
productName: Scalars['String']['output'];
@@ -247,10 +273,13 @@ export type SchemaQuery = {
allProducts: Array<SchemaProduct>;
allOrders: Array<SchemaOrder>;
orderList: Array<SchemaOrder>;
product: SchemaProduct;
orderStats: SchemaOrderStats;
productStats: Array<SchemaProductOrderStat>;
allUsers: Array<SchemaUser>;
user: SchemaUser;
/** The logged in user, or null when nobody is logged in */
me?: Maybe<SchemaViewer>;
};
@@ -283,10 +312,22 @@ export type SchemaQueryOrderListArgs = {
};
export type SchemaQueryProductArgs = {
productId: Scalars['ID']['input'];
};
export type SchemaQueryUserArgs = {
userId: Scalars['ID']['input'];
};
export type SchemaSaveProductResponse = SchemaSaveProductSuccess | SchemaFormErrors;
export type SchemaSaveProductSuccess = {
__typename?: 'SaveProductSuccess';
product: SchemaProduct;
};
export type SchemaTopMonth = {
__typename?: 'TopMonth';
period: Scalars['String']['output'];
@@ -330,6 +371,14 @@ export type SchemaUserProfile = {
productOrderStats: Array<SchemaProductOrderStat>;
};
export type SchemaViewer = {
__typename?: 'Viewer';
id: Scalars['ID']['output'];
firstName: Scalars['String']['output'];
canAddProducts: Scalars['Boolean']['output'];
canChangeProducts: Scalars['Boolean']['output'];
};
export type InventoryTransactionInput = {
productId: string | number;
amount: number;
@@ -342,6 +391,16 @@ export type OrderLineInput = {
amount: number;
};
export type ProductInput = {
name: string;
salePriceInt: string;
salePriceExt: string;
volumeLiter?: string | null | undefined;
alcoholPercent?: string | null | undefined;
description?: string;
active?: boolean;
};
export type CreateUserMutationVariables = Exact<{
firstName: string;
lastName: string;
@@ -351,6 +410,11 @@ export type CreateUserMutationVariables = Exact<{
export type CreateUserMutation = { __typename: 'Mutation', createUser: { __typename: 'User', id: string, firstName: string, lastName: string, profile: { __typename: 'UserProfile', id: string } | null } };
export type ViewerQueryVariables = Exact<{ [key: string]: never; }>;
export type ViewerQuery = { __typename: 'Query', me: { __typename: 'Viewer', id: string, firstName: string, canAddProducts: boolean, canChangeProducts: boolean } | null };
export type CreateDepositMutationVariables = Exact<{
userId: string | number;
amount: number;
@@ -373,6 +437,16 @@ export type CreateInventoryTransactionMutation = { __typename: 'Mutation', creat
| { __typename: 'FormErrors', message: string, fields: Array<{ __typename: 'FieldError', field: string, message: string }> }
};
export type CreateProductMutationVariables = Exact<{
data: ProductInput;
}>;
export type CreateProductMutation = { __typename: 'Mutation', createProduct:
| { __typename: 'SaveProductSuccess', product: { __typename: 'Product', id: string, name: string, salePriceInt: string, salePriceExt: string, volumeLiter: string | null, alcoholPercent: string | null, description: string, active: boolean, inventoryAmount: number, updated: string } }
| { __typename: 'FormErrors', message: string, fields: Array<{ __typename: 'FieldError', field: string, message: string }> }
};
export type InventoryPageQueryVariables = Exact<{
limit?: number | null | undefined;
offset?: number | null | undefined;
@@ -394,6 +468,24 @@ export type InventoryReportQueryVariables = Exact<{
export type InventoryReportQuery = { __typename: 'Query', inventoryReport: { __typename: 'InventoryReport', inventoryPurchases: string, productSales: string, deposits: string, orderPurchases: string, balanceAtStart: string, balanceAtEnd: string, products: Array<{ __typename: 'InventoryReportProduct', productId: string, productName: string, purchaseUnits: number, purchaseValue: string, salesUnits: number, salesValue: string }> } };
export type ProductFormQueryVariables = Exact<{
productId: string | number;
}>;
export type ProductFormQuery = { __typename: 'Query', product: { __typename: 'Product', id: string, name: string, salePriceInt: string, salePriceExt: string, volumeLiter: string | null, alcoholPercent: string | null, description: string, active: boolean } };
export type UpdateProductMutationVariables = Exact<{
productId: string | number;
data: ProductInput;
}>;
export type UpdateProductMutation = { __typename: 'Mutation', updateProduct:
| { __typename: 'SaveProductSuccess', product: { __typename: 'Product', id: string, name: string, salePriceInt: string, salePriceExt: string, volumeLiter: string | null, alcoholPercent: string | null, description: string, active: boolean, inventoryAmount: number, updated: string } }
| { __typename: 'FormErrors', message: string, fields: Array<{ __typename: 'FieldError', field: string, message: string }> }
};
export type OrderListQueryVariables = Exact<{
limit?: number | null | undefined;
offset?: number | null | undefined;
@@ -464,11 +556,15 @@ export type UserDetailQuery = { __typename: 'Query', user: { __typename: 'User',
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<CreateUserMutation, CreateUserMutationVariables>;
export const ViewerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"viewer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"canAddProducts"}},{"kind":"Field","name":{"kind":"Name","value":"canChangeProducts"}}]}}]}}]} as unknown as DocumentNode<ViewerQuery, ViewerQueryVariables>;
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<CreateDepositMutation, CreateDepositMutationVariables>;
export const CreateInventoryTransactionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"createInventoryTransaction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"InventoryTransactionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createInventoryTransaction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CreateInventoryTransactionSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inventoryTransaction"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"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":"active"}},{"kind":"Field","name":{"kind":"Name","value":"inventoryAmount"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FormErrors"}},"selectionSet":{"kind":"SelectionSet","selections":[{"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<CreateInventoryTransactionMutation, CreateInventoryTransactionMutationVariables>;
export const CreateProductDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"createProduct"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProductInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createProduct"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SaveProductSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"salePriceInt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"volumeLiter"}},{"kind":"Field","name":{"kind":"Name","value":"alcoholPercent"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"inventoryAmount"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FormErrors"}},"selectionSet":{"kind":"SelectionSet","selections":[{"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<CreateProductMutation, CreateProductMutationVariables>;
export const InventoryPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"inventoryPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"20"}},{"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":"allProducts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"inventoryAmount"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"wholesaleUnitPrice"}},{"kind":"Field","name":{"kind":"Name","value":"wholesaleValue"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inventoryTransactionList"},"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":"unitPrice"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"product"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}}]}}]}}]}}]} as unknown as DocumentNode<InventoryPageQuery, InventoryPageQueryVariables>;
export const InventoryProductsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"inventoryProducts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allProducts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"inventoryAmount"}}]}}]}}]} as unknown as DocumentNode<InventoryProductsQuery, InventoryProductsQueryVariables>;
export const InventoryReportDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"inventoryReport"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"startTime"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DateTime"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"endTime"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DateTime"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inventoryReport"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"startTime"},"value":{"kind":"Variable","name":{"kind":"Name","value":"startTime"}}},{"kind":"Argument","name":{"kind":"Name","value":"endTime"},"value":{"kind":"Variable","name":{"kind":"Name","value":"endTime"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"products"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productId"}},{"kind":"Field","name":{"kind":"Name","value":"productName"}},{"kind":"Field","name":{"kind":"Name","value":"purchaseUnits"}},{"kind":"Field","name":{"kind":"Name","value":"purchaseValue"}},{"kind":"Field","name":{"kind":"Name","value":"salesUnits"}},{"kind":"Field","name":{"kind":"Name","value":"salesValue"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inventoryPurchases"}},{"kind":"Field","name":{"kind":"Name","value":"productSales"}},{"kind":"Field","name":{"kind":"Name","value":"deposits"}},{"kind":"Field","name":{"kind":"Name","value":"orderPurchases"}},{"kind":"Field","name":{"kind":"Name","value":"balanceAtStart"}},{"kind":"Field","name":{"kind":"Name","value":"balanceAtEnd"}}]}}]}}]} as unknown as DocumentNode<InventoryReportQuery, InventoryReportQueryVariables>;
export const ProductFormDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"productForm"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"productId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"product"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"productId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"productId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceInt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"volumeLiter"}},{"kind":"Field","name":{"kind":"Name","value":"alcoholPercent"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"active"}}]}}]}}]} as unknown as DocumentNode<ProductFormQuery, ProductFormQueryVariables>;
export const UpdateProductDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"updateProduct"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"productId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProductInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateProduct"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"productId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"productId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SaveProductSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"salePriceInt"}},{"kind":"Field","name":{"kind":"Name","value":"salePriceExt"}},{"kind":"Field","name":{"kind":"Name","value":"volumeLiter"}},{"kind":"Field","name":{"kind":"Name","value":"alcoholPercent"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"inventoryAmount"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FormErrors"}},"selectionSet":{"kind":"SelectionSet","selections":[{"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<UpdateProductMutation, UpdateProductMutationVariables>;
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<OrderListQuery, OrderListQueryVariables>;
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<TransactionListQuery, TransactionListQueryVariables>;
export const AllProductsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allProducts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allProducts"},"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"}},{"kind":"Field","name":{"kind":"Name","value":"active"}}]}}]}}]} as unknown as DocumentNode<AllProductsQuery, AllProductsQueryVariables>;
+32
View File
@@ -101,6 +101,8 @@ scalar JSON @specifiedBy(url: "https://ecma-international.org/wp-content/uploads
type Mutation {
createInventoryTransaction(data: InventoryTransactionInput!): CreateInventoryTransactionResponse!
createProduct(data: ProductInput!): SaveProductResponse!
updateProduct(productId: ID!, data: ProductInput!): SaveProductResponse!
createOrder(customerId: ID!, orderLines: [OrderLineInput!]!, isExternal: Boolean! = false): CreateOrderResponse!
createDeposit(userId: ID!, amount: Int!): CreateDepositResponse!
createUser(input: UserInput!): User!
@@ -166,6 +168,9 @@ type Point {
type Product {
id: ID!
name: String!
description: String!
volumeLiter: Decimal
alcoholPercent: Decimal
imageUrl: String!
salePriceInt: Decimal!
salePriceExt: Decimal!
@@ -186,6 +191,16 @@ input ProductFilter {
DISTINCT: Boolean
}
input ProductInput {
name: String!
salePriceInt: Decimal!
salePriceExt: Decimal!
volumeLiter: Decimal = null
alcoholPercent: Decimal = null
description: String! = ""
active: Boolean! = true
}
type ProductOrderStat {
productName: String!
data: [Point!]!
@@ -198,10 +213,20 @@ type Query {
allProducts(filters: ProductFilter): [Product!]!
allOrders: [Order!]!
orderList(ordering: [OrderOrdering!]! = [], pagination: OffsetPaginationInput): [Order!]!
product(productId: ID!): Product!
orderStats: OrderStats!
productStats: [ProductOrderStat!]!
allUsers: [User!]!
user(userId: ID!): User!
"""The logged in user, or null when nobody is logged in"""
me: Viewer
}
union SaveProductResponse = SaveProductSuccess | FormErrors
type SaveProductSuccess {
product: Product!
}
type TopMonth {
@@ -242,4 +267,11 @@ type UserProfile {
orderSumTotal: Decimal!
topMonths: [TopMonth!]!
productOrderStats: [ProductOrderStat!]!
}
type Viewer {
id: ID!
firstName: String!
canAddProducts: Boolean!
canChangeProducts: Boolean!
}