* Feed preorder data to product variant forms

* Add end preorder date input and handle date data

* Translate strings, refactor date parsing

* Fix snapshots

* CR response

* CR response

* CR response

* Fix negative threshold, product variant preorder toggle, product variant update, and simple product creation

* Make preorder data optional

* Prevent setting past date as preorder end

* Disable replacing preorder variant in order

* Adjust fulfill view to preorder in variant

* CR response + prevent subbmiting form when endPreorderDate is in the past and display warning

* Add ErrorNoticeBar

* Translate preorder end date in past error message, fix form submissison disabling logic

* Rebase fixes

* Fix preorder form disabling logic, remove isPreorder field

* Fix edge cases aroud preorder inputs

* Update storyshots
This commit is contained in:
JanChodorowski 2021-10-01 14:41:31 +02:00 committed by GitHub
parent 6ddb537445
commit 25f7c8e4d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
82 changed files with 2714 additions and 1074 deletions

View file

@ -5653,6 +5653,9 @@
"src_dot_plugins_dot_views_dot_955370043": {
"string": "The plugin may stop working after this field is cleared. Are you sure you want to proceed?"
},
"src_dot_preorderEndDateInFutureErrorText": {
"string": "Preorder end time needs to be set in the future"
},
"src_dot_productTypes": {
"context": "product types section name",
"string": "Product Types"
@ -6109,6 +6112,17 @@
"context": "product weight",
"string": "Weight"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_1540024536": {
"context": "product inventory, checkbox",
"string": "Variant currently in preorder"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_2473321447": {
"string": "CANCEL END DATE"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_2481295410": {
"context": "app has been installed",
"string": "{unitsLeft} units left"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_2585918415": {
"string": "SKU (Stock Keeping Unit)"
},
@ -6116,10 +6130,24 @@
"context": "tabel column header",
"string": "Warehouse Name"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_2699291877": {
"string": "SETUP END DATE"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_2796503714": {
"context": "header",
"string": "Quantity"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_294624093": {
"context": "info text",
"string": "Preordered products will be available in all warehouses. You can set a threshold for sold quantity. Leaving input blank will be interpreted as no limit to sale. Sold items will be allocated at the warehouse assigned to chosen shipping zone."
},
"src_dot_products_dot_components_dot_ProductStocks_dot_2999762286": {
"string": "Threshold that cannot be exceeded even if per channel thresholds are still available"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_3420537107": {
"context": "tabel column header",
"string": "Channels"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_3633706025": {
"context": "product inventory, checkbox",
"string": "Track Inventory"
@ -6128,6 +6156,16 @@
"context": "button",
"string": "Assign Warehouse"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_3862021157": {
"context": "info text",
"string": "Set up an end date of preorder. When end date will be reached product will be automatically taken from preorder to standard selling"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_4187357147": {
"string": "Unlimited"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_578055997": {
"string": "Global threshold"
},
"src_dot_products_dot_components_dot_ProductStocks_dot_849869830": {
"string": "Active inventory tracking will automatically calculate changes of stock"
},
@ -6318,6 +6356,17 @@
"context": "dialog header",
"string": "Delete Variant"
},
"src_dot_products_dot_components_dot_ProductVariantEndPreorderDialog_dot_dialogConfirmButtonLabel": {
"context": "button label",
"string": "ACCEPT"
},
"src_dot_products_dot_components_dot_ProductVariantEndPreorderDialog_dot_dialogMessage": {
"string": "You are about to end your products preorder. You have sold {variantGlobalSoldUnits} units of this variant. Sold units will be allocated at appropriate warehouses. Remember to add remaining threshold stock to warehouses."
},
"src_dot_products_dot_components_dot_ProductVariantEndPreorderDialog_dot_dialogTitle": {
"context": "dialog header",
"string": "Ending preorder"
},
"src_dot_products_dot_components_dot_ProductVariantImageSelectDialog_dot_2015102342": {
"context": "dialog header",
"string": "Media Selection"
@ -8101,10 +8150,18 @@
"context": "table column header, allocated product quantity",
"string": "Allocated"
},
"tableColChannelThreshold": {
"context": "table column header",
"string": "Channel threshold"
},
"tableColQuantity": {
"context": "table column header",
"string": "Quantity"
},
"tableColSoldUnits": {
"context": "table column header, sold units preorder quantity",
"string": "Sold units"
},
"transaction reference subtitle": {
"context": "transaction reference subtitle",
"string": "Transaction reference"
@ -8113,6 +8170,10 @@
"context": "product unavailability",
"string": "Unavailable for purchase"
},
"unlimitedUnitsLeft": {
"context": "section header",
"string": "Unlimited"
},
"voucherDetailsUnassignCategory": {
"context": "unassign category from voucher, button",
"string": "Unassign"

View file

@ -3795,6 +3795,7 @@ type Mutation {
productVariantTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): ProductVariantTranslate
productVariantChannelListingUpdate(id: ID!, input: [ProductVariantChannelListingAddInput!]!): ProductVariantChannelListingUpdate
productVariantReorderAttributeValues(attributeId: ID!, moves: [ReorderInput]!, variantId: ID!): ProductVariantReorderAttributeValues
productVariantPreorderDeactivate(id: ID!): ProductVariantPreorderDeactivate
variantMediaAssign(mediaId: ID!, variantId: ID!): VariantMediaAssign
variantMediaUnassign(mediaId: ID!, variantId: ID!): VariantMediaUnassign
paymentCapture(amount: PositiveDecimal, paymentId: ID!): PaymentCapture
@ -5127,6 +5128,22 @@ enum PostalCodeRuleInclusionTypeEnum {
EXCLUDE
}
type PreorderData {
globalThreshold: Int
globalSoldUnits: Int!
endDate: DateTime
}
input PreorderSettingsInput {
globalThreshold: Int
endDate: DateTime
}
type PreorderThreshold {
quantity: Int
soldUnits: Int!
}
input PriceInput {
currency: String!
amount: PositiveDecimal!
@ -5310,6 +5327,7 @@ enum ProductErrorCode {
CANNOT_MANAGE_PRODUCT_WITHOUT_VARIANT
PRODUCT_NOT_ASSIGNED_TO_CHANNEL
UNSUPPORTED_MEDIA_PROVIDER
PREORDER_VARIANT_CANNOT_BE_DEACTIVATED
}
enum ProductFieldEnum {
@ -5342,6 +5360,7 @@ input ProductFilterInput {
productTypes: [ID]
giftCard: Boolean
ids: [ID]
hasPreorderedVariants: Boolean
channel: String
}
@ -5628,6 +5647,7 @@ type ProductVariant implements Node & ObjectWithMetadata {
digitalContent: DigitalContent
stocks(address: AddressInput, countryCode: CountryCode): [Stock]
quantityAvailable(address: AddressInput, countryCode: CountryCode): Int!
preorder: PreorderData
}
type ProductVariantBulkCreate {
@ -5642,6 +5662,7 @@ input ProductVariantBulkCreateInput {
sku: String
trackInventory: Boolean
weight: WeightScalar
preorder: PreorderSettingsInput
stocks: [StockInput!]
channelListings: [ProductVariantChannelListingAddInput!]
}
@ -5658,12 +5679,14 @@ type ProductVariantChannelListing implements Node {
price: Money
costPrice: Money
margin: Int
preorderThreshold: PreorderThreshold
}
input ProductVariantChannelListingAddInput {
channelId: ID!
price: PositiveDecimal!
costPrice: PositiveDecimal
preorderThreshold: Int
}
type ProductVariantChannelListingUpdate {
@ -5694,6 +5717,7 @@ input ProductVariantCreateInput {
sku: String
trackInventory: Boolean
weight: WeightScalar
preorder: PreorderSettingsInput
product: ID!
stocks: [StockInput!]
}
@ -5708,6 +5732,7 @@ input ProductVariantFilterInput {
search: String
sku: [String]
metadata: [MetadataFilter]
isPreorder: Boolean
}
input ProductVariantInput {
@ -5715,6 +5740,12 @@ input ProductVariantInput {
sku: String
trackInventory: Boolean
weight: WeightScalar
preorder: PreorderSettingsInput
}
type ProductVariantPreorderDeactivate {
productVariant: ProductVariant
errors: [ProductError!]!
}
type ProductVariantReorder {

View file

@ -34,6 +34,8 @@ export interface ChannelData {
availableForPurchase?: string;
isAvailableForPurchase?: boolean;
visibleInListings?: boolean;
preorderThreshold?: number;
unitsSold?: number;
}
export interface ChannelPriceData {
@ -53,6 +55,34 @@ export type ChannelPriceArgs = RequireOnlyOne<
"price" | "costPrice"
>;
export interface ChannelPreorderArgs {
preorderThreshold: number;
unitsSold: number;
hasPreorderEndDate: boolean;
preorderEndDateTime?: string;
}
export interface ChannelPriceAndPreorderData {
id: string;
name: string;
currency: string;
price: string;
costPrice?: string;
preorderThreshold?: number | null;
unitsSold?: number;
}
export interface IChannelPriceAndPreorderArgs {
price: string;
costPrice: string;
preorderThreshold?: number | null;
unitsSold?: number;
}
export type ChannelPriceAndPreorderArgs = IChannelPriceArgs & {
preorderThreshold: number | null;
unitsSold?: number;
};
export interface ChannelVoucherData {
id: string;
name: string;
@ -262,6 +292,8 @@ export const createChannelsDataFromProduct = (
productData.variants,
channel.id
);
const soldUnits = variantChannel?.preorderThreshold?.soldUnits;
const preorderThreshold = variantChannel?.preorderThreshold?.quantity;
return {
availableForPurchase,
@ -274,7 +306,9 @@ export const createChannelsDataFromProduct = (
isAvailableForPurchase: !!isAvailableForPurchase,
name: channel.name,
price: price ? price.amount.toString() : "",
visibleInListings: !!visibleInListings
visibleInListings: !!visibleInListings,
soldUnits,
preorderThreshold
};
}
) || [];

View file

@ -0,0 +1,87 @@
import { TextField } from "@material-ui/core";
import { TextFieldProps } from "@material-ui/core/TextField";
import { commonMessages } from "@saleor/intl";
import { makeStyles } from "@saleor/macaw-ui";
import { DateTime, joinDateTime, splitDateTime } from "@saleor/misc";
import React, { useEffect, useState } from "react";
import { useIntl } from "react-intl";
import ErrorNoticeBar from "./ErrorNoticeBar";
type DateTimeFieldProps = Omit<TextFieldProps, "label" | "error"> & {
onChange: (value: string) => void;
error: string | React.ReactNode;
setError?: () => void;
futureDatesOnly?: boolean;
value: string;
};
const useStyles = makeStyles(
theme => ({
dateInput: {
marginRight: theme.spacing(2)
},
errorNoticeBar: {
marginTop: theme.spacing(2)
}
}),
{ name: "DateTimeTimezoneField" }
);
export const DateTimeTimezoneField: React.FC<DateTimeFieldProps> = ({
disabled,
name,
onChange,
error,
fullWidth,
value: initialValue
}) => {
const classes = useStyles({});
const intl = useIntl();
const [value, setValue] = useState<DateTime>(
initialValue ? splitDateTime(initialValue) : { date: "", time: "" }
);
useEffect(() => {
const newDate = joinDateTime(value.date, value.time);
onChange(newDate);
}, [value]);
return (
<>
<TextField
className={classes.dateInput}
fullWidth={fullWidth}
disabled={disabled}
error={!!error}
label={intl.formatMessage(commonMessages.date)}
name={`${name}:date`}
onChange={event => {
const date = event.target.value;
setValue(value => ({ ...value, date }));
}}
type="date"
value={value.date}
InputLabelProps={{ shrink: true }}
/>
<TextField
fullWidth={fullWidth}
disabled={disabled}
error={!!error}
label={intl.formatMessage(commonMessages.time)}
name={`${name}:time`}
onChange={event => {
const time = event.target.value;
setValue(value => ({ ...value, time }));
}}
type="time"
value={value.time}
InputLabelProps={{ shrink: true }}
/>
{error && (
<ErrorNoticeBar className={classes.errorNoticeBar} message={error} />
)}
</>
);
};

View file

@ -0,0 +1,34 @@
import { Card, CardContent, Typography } from "@material-ui/core";
import { makeStyles } from "@saleor/macaw-ui";
import classNames from "classnames";
import React from "react";
interface ErrorNoticeBarProps {
className?: string;
message: string | React.ReactNode;
}
const useStyles = makeStyles(
theme => ({
root: {
background: theme.palette.alert.paper.error
}
}),
{
name: "ErrorNoticeBar"
}
);
const ErrorNoticeBar: React.FC<ErrorNoticeBarProps> = props => {
const { className, message } = props;
const classes = useStyles(props);
return (
<Card className={classNames(classes.root, className)}>
<CardContent>
<Typography variant="body1">{message}</Typography>
</CardContent>
</Card>
);
};
ErrorNoticeBar.displayName = "ErrorNoticeBar";
export default ErrorNoticeBar;

View file

@ -0,0 +1 @@
export { default } from "./ErrorNoticeBar";

View file

@ -81,6 +81,9 @@ export const fragmentOrderLine = gql`
variant {
id
quantityAvailable
preorder {
endDate
}
}
productName
productSku

View file

@ -27,6 +27,14 @@ export const fragmentMoney = gql`
}
`;
export const fragmentPreorder = gql`
fragment PreorderFragment on PreorderData {
globalThreshold
globalSoldUnits
endDate
}
`;
export const priceRangeFragment = gql`
${fragmentMoney}
fragment PriceRangeFragment on TaxedMoneyRange {
@ -95,6 +103,10 @@ export const channelListingProductVariantFragment = gql`
costPrice {
...Money
}
preorderThreshold {
quantity
soldUnits
}
}
`;
@ -178,6 +190,7 @@ export const productVariantAttributesFragment = gql`
`;
export const productFragmentDetails = gql`
${fragmentPreorder}
${fragmentProductMedia}
${productVariantAttributesFragment}
${stockFragment}
@ -226,6 +239,9 @@ export const productFragmentDetails = gql`
...StockFragment
}
trackInventory
preorder {
...PreorderFragment
}
channelListings {
...ChannelListingProductVariantFragment
}
@ -282,6 +298,7 @@ export const selectedVariantAttributeFragment = gql`
`;
export const fragmentVariant = gql`
${fragmentPreorder}
${fragmentProductMedia}
${selectedVariantAttributeFragment}
${priceRangeFragment}
@ -357,6 +374,9 @@ export const fragmentVariant = gql`
...StockFragment
}
trackInventory
preorder {
...PreorderFragment
}
weight {
...WeightFragment
}

View file

@ -26,9 +26,16 @@ export interface ChannelListingProductVariantFragment_costPrice {
currency: string;
}
export interface ChannelListingProductVariantFragment_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface ChannelListingProductVariantFragment {
__typename: "ProductVariantChannelListing";
channel: ChannelListingProductVariantFragment_channel;
price: ChannelListingProductVariantFragment_price | null;
costPrice: ChannelListingProductVariantFragment_costPrice | null;
preorderThreshold: ChannelListingProductVariantFragment_preorderThreshold | null;
}

View file

@ -9,10 +9,16 @@ import { DiscountValueTypeEnum, FulfillmentStatus } from "./../../types/globalTy
// GraphQL fragment: FulfillmentFragment
// ====================================================
export interface FulfillmentFragment_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface FulfillmentFragment_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: FulfillmentFragment_lines_orderLine_variant_preorder | null;
}
export interface FulfillmentFragment_lines_orderLine_unitDiscount {

View file

@ -206,10 +206,16 @@ export interface OrderDetailsFragment_events {
lines: (OrderDetailsFragment_events_lines | null)[] | null;
}
export interface OrderDetailsFragment_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDetailsFragment_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDetailsFragment_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderDetailsFragment_fulfillments_lines_orderLine_unitDiscount {
@ -302,10 +308,16 @@ export interface OrderDetailsFragment_fulfillments {
warehouse: OrderDetailsFragment_fulfillments_warehouse | null;
}
export interface OrderDetailsFragment_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDetailsFragment_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDetailsFragment_lines_variant_preorder | null;
}
export interface OrderDetailsFragment_lines_unitDiscount {

View file

@ -9,10 +9,16 @@ import { DiscountValueTypeEnum } from "./../../types/globalTypes";
// GraphQL fragment: OrderLineFragment
// ====================================================
export interface OrderLineFragment_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineFragment_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineFragment_variant_preorder | null;
}
export interface OrderLineFragment_unitDiscount {

View file

@ -0,0 +1,15 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL fragment: PreorderFragment
// ====================================================
export interface PreorderFragment {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}

View file

@ -260,6 +260,13 @@ export interface Product_variants_stocks {
warehouse: Product_variants_stocks_warehouse;
}
export interface Product_variants_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface Product_variants_channelListings_channel {
__typename: "Channel";
id: string;
@ -279,11 +286,18 @@ export interface Product_variants_channelListings_costPrice {
currency: string;
}
export interface Product_variants_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface Product_variants_channelListings {
__typename: "ProductVariantChannelListing";
channel: Product_variants_channelListings_channel;
price: Product_variants_channelListings_price | null;
costPrice: Product_variants_channelListings_costPrice | null;
preorderThreshold: Product_variants_channelListings_preorderThreshold | null;
}
export interface Product_variants {
@ -295,6 +309,7 @@ export interface Product_variants {
media: Product_variants_media[] | null;
stocks: (Product_variants_stocks | null)[] | null;
trackInventory: boolean;
preorder: Product_variants_preorder | null;
channelListings: Product_variants_channelListings[] | null;
}

View file

@ -299,11 +299,18 @@ export interface ProductVariant_channelListings_costPrice {
currency: string;
}
export interface ProductVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface ProductVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: ProductVariant_channelListings_channel;
price: ProductVariant_channelListings_price | null;
costPrice: ProductVariant_channelListings_costPrice | null;
preorderThreshold: ProductVariant_channelListings_preorderThreshold | null;
}
export interface ProductVariant_stocks_warehouse {
@ -320,6 +327,13 @@ export interface ProductVariant_stocks {
warehouse: ProductVariant_stocks_warehouse;
}
export interface ProductVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface ProductVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -340,5 +354,6 @@ export interface ProductVariant {
sku: string | null;
stocks: (ProductVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: ProductVariant_preorder | null;
weight: ProductVariant_weight | null;
}

View file

@ -135,6 +135,9 @@ export const errorMessages = defineMessages({
imageUploadErrorText: {
defaultMessage:
"There was a poblem with the file you uploaded as an image and it couldn't be used. Please try a different file."
},
preorderEndDateInFutureErrorText: {
defaultMessage: "Preorder end time needs to be set in the future"
}
});

View file

@ -296,6 +296,7 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
0
);
const overfulfill = remainingQuantity < quantityToFulfill;
const isPreorder = !!line.variant?.preorder;
return (
<TableRow key={line.id}>
@ -318,6 +319,18 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
{line.variant?.sku}
</TableCell>
{warehouses?.map(warehouse => {
if (isPreorder) {
return (
<TableCell
key="skeleton"
className={classNames(
classes.colQuantity,
classes.error
)}
/>
);
}
const warehouseStock = line.variant?.stocks?.find(
stock => stock.warehouse.id === warehouse.id
);
@ -419,6 +432,8 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
className={classes.colQuantityTotal}
key="total"
>
{!isPreorder && (
<>
<span
className={classNames({
[classes.error]: overfulfill,
@ -429,6 +444,8 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
{quantityToFulfill}
</span>{" "}
/ {remainingQuantity}
</>
)}
</TableCell>
</TableRow>
);

View file

@ -24,6 +24,7 @@ export const orderToFulfill: OrderFulfillData_order = {
id: "UHJvZHVjdFZhcmlhbnQ6Mjk2",
name: "S",
sku: "62783187",
preorder: null,
attributes: [
{
__typename: "SelectedAttribute",
@ -87,6 +88,7 @@ export const orderToFulfill: OrderFulfillData_order = {
id: "UHJvZHVjdFZhcmlhbnQ6MTgx",
name: "2.5l",
sku: "998323583",
preorder: null,
attributes: [
{
__typename: "SelectedAttribute",
@ -143,6 +145,7 @@ export const orderToFulfill: OrderFulfillData_order = {
id: "UHJvZHVjdFZhcmlhbnQ6MTgy",
name: "5l",
sku: "998323584",
preorder: null,
attributes: [
{
__typename: "SelectedAttribute",

View file

@ -175,6 +175,7 @@ const ItemsCard: React.FC<OrderReturnRefundLinesCardProps> = ({
.isRefunded;
const isReplacable = !!variant && !isRefunded;
const isReturnable = !!variant;
const isPreorder = !!variant?.preorder;
const lineQuantity = fulfilmentId ? quantity : quantityToFulfill;
const isSelected = itemsSelections.find(getById(id))?.value;
const currentQuantity = itemsQuantities.find(getById(id))?.value;
@ -233,7 +234,7 @@ const ItemsCard: React.FC<OrderReturnRefundLinesCardProps> = ({
)}
</TableCell>
<TableCell align="center">
{isReplacable && (
{isReplacable && !isPreorder && (
<Checkbox
checked={isSelected}
onChange={() => onChangeSelected(id, !isSelected)}

View file

@ -1103,7 +1103,8 @@ export const order = (placeholder: string): OrderDetails_order => ({
variant: {
__typename: "ProductVariant",
id: "dsfsfuhb",
quantityAvailable: 10
quantityAvailable: 10,
preorder: null
}
},
quantity: 1
@ -1172,7 +1173,8 @@ export const order = (placeholder: string): OrderDetails_order => ({
variant: {
__typename: "ProductVariant",
id: "dsfsfuhb",
quantityAvailable: 10
quantityAvailable: 10,
preorder: null
}
},
quantity: 1
@ -1248,7 +1250,8 @@ export const order = (placeholder: string): OrderDetails_order => ({
variant: {
__typename: "ProductVariant",
id: "dsfsfuhb",
quantityAvailable: 10
quantityAvailable: 10,
preorder: null
}
},
{
@ -1303,7 +1306,8 @@ export const order = (placeholder: string): OrderDetails_order => ({
variant: {
__typename: "ProductVariant",
id: "dsfsfuhb",
quantityAvailable: 10
quantityAvailable: 10,
preorder: null
}
}
],
@ -1491,7 +1495,8 @@ export const draftOrder = (placeholder: string): OrderDetails_order => ({
variant: {
__typename: "ProductVariant",
id: "dsfsfuhb",
quantityAvailable: 10
quantityAvailable: 10,
preorder: null
}
},
{
@ -1545,7 +1550,8 @@ export const draftOrder = (placeholder: string): OrderDetails_order => ({
variant: {
__typename: "ProductVariant",
id: "dsfsfuhb",
quantityAvailable: 10
quantityAvailable: 10,
preorder: null
}
}
],

View file

@ -254,6 +254,9 @@ const orderFulfillData = gql`
id
name
sku
preorder {
endDate
}
attributes {
values {
id

View file

@ -215,10 +215,16 @@ export interface FulfillOrder_orderFulfill_order_events {
lines: (FulfillOrder_orderFulfill_order_events_lines | null)[] | null;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitDiscount {
@ -311,10 +317,16 @@ export interface FulfillOrder_orderFulfill_order_fulfillments {
warehouse: FulfillOrder_orderFulfill_order_fulfillments_warehouse | null;
}
export interface FulfillOrder_orderFulfill_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface FulfillOrder_orderFulfill_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: FulfillOrder_orderFulfill_order_lines_variant_preorder | null;
}
export interface FulfillOrder_orderFulfill_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderCancel_orderCancel_order_events {
lines: (OrderCancel_orderCancel_order_events_lines | null)[] | null;
}
export interface OrderCancel_orderCancel_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderCancel_orderCancel_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderCancel_orderCancel_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderCancel_orderCancel_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderCancel_orderCancel_order_fulfillments {
warehouse: OrderCancel_orderCancel_order_fulfillments_warehouse | null;
}
export interface OrderCancel_orderCancel_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderCancel_orderCancel_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderCancel_orderCancel_order_lines_variant_preorder | null;
}
export interface OrderCancel_orderCancel_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderCapture_orderCapture_order_events {
lines: (OrderCapture_orderCapture_order_events_lines | null)[] | null;
}
export interface OrderCapture_orderCapture_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderCapture_orderCapture_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderCapture_orderCapture_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderCapture_orderCapture_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderCapture_orderCapture_order_fulfillments {
warehouse: OrderCapture_orderCapture_order_fulfillments_warehouse | null;
}
export interface OrderCapture_orderCapture_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderCapture_orderCapture_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderCapture_orderCapture_order_lines_variant_preorder | null;
}
export interface OrderCapture_orderCapture_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderConfirm_orderConfirm_order_events {
lines: (OrderConfirm_orderConfirm_order_events_lines | null)[] | null;
}
export interface OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderConfirm_orderConfirm_order_fulfillments {
warehouse: OrderConfirm_orderConfirm_order_fulfillments_warehouse | null;
}
export interface OrderConfirm_orderConfirm_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderConfirm_orderConfirm_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderConfirm_orderConfirm_order_lines_variant_preorder | null;
}
export interface OrderConfirm_orderConfirm_order_lines_unitDiscount {

View file

@ -206,10 +206,16 @@ export interface OrderDetails_order_events {
lines: (OrderDetails_order_events_lines | null)[] | null;
}
export interface OrderDetails_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDetails_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDetails_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderDetails_order_fulfillments_lines_orderLine_unitDiscount {
@ -302,10 +308,16 @@ export interface OrderDetails_order_fulfillments {
warehouse: OrderDetails_order_fulfillments_warehouse | null;
}
export interface OrderDetails_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDetails_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDetails_order_lines_variant_preorder | null;
}
export interface OrderDetails_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderDiscountAdd_orderDiscountAdd_order_events {
lines: (OrderDiscountAdd_orderDiscountAdd_order_events_lines | null)[] | null;
}
export interface OrderDiscountAdd_orderDiscountAdd_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDiscountAdd_orderDiscountAdd_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDiscountAdd_orderDiscountAdd_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderDiscountAdd_orderDiscountAdd_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderDiscountAdd_orderDiscountAdd_order_fulfillments {
warehouse: OrderDiscountAdd_orderDiscountAdd_order_fulfillments_warehouse | null;
}
export interface OrderDiscountAdd_orderDiscountAdd_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDiscountAdd_orderDiscountAdd_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDiscountAdd_orderDiscountAdd_order_lines_variant_preorder | null;
}
export interface OrderDiscountAdd_orderDiscountAdd_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderDiscountDelete_orderDiscountDelete_order_events {
lines: (OrderDiscountDelete_orderDiscountDelete_order_events_lines | null)[] | null;
}
export interface OrderDiscountDelete_orderDiscountDelete_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDiscountDelete_orderDiscountDelete_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDiscountDelete_orderDiscountDelete_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderDiscountDelete_orderDiscountDelete_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderDiscountDelete_orderDiscountDelete_order_fulfillments {
warehouse: OrderDiscountDelete_orderDiscountDelete_order_fulfillments_warehouse | null;
}
export interface OrderDiscountDelete_orderDiscountDelete_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDiscountDelete_orderDiscountDelete_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDiscountDelete_orderDiscountDelete_order_lines_variant_preorder | null;
}
export interface OrderDiscountDelete_orderDiscountDelete_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderDiscountUpdate_orderDiscountUpdate_order_events {
lines: (OrderDiscountUpdate_orderDiscountUpdate_order_events_lines | null)[] | null;
}
export interface OrderDiscountUpdate_orderDiscountUpdate_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDiscountUpdate_orderDiscountUpdate_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDiscountUpdate_orderDiscountUpdate_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderDiscountUpdate_orderDiscountUpdate_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderDiscountUpdate_orderDiscountUpdate_order_fulfillments {
warehouse: OrderDiscountUpdate_orderDiscountUpdate_order_fulfillments_warehouse | null;
}
export interface OrderDiscountUpdate_orderDiscountUpdate_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDiscountUpdate_orderDiscountUpdate_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDiscountUpdate_orderDiscountUpdate_order_lines_variant_preorder | null;
}
export interface OrderDiscountUpdate_orderDiscountUpdate_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderDraftCancel_draftOrderDelete_order_events {
lines: (OrderDraftCancel_draftOrderDelete_order_events_lines | null)[] | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderDraftCancel_draftOrderDelete_order_fulfillments {
warehouse: OrderDraftCancel_draftOrderDelete_order_fulfillments_warehouse | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDraftCancel_draftOrderDelete_order_lines_variant_preorder | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderDraftFinalize_draftOrderComplete_order_events {
lines: (OrderDraftFinalize_draftOrderComplete_order_events_lines | null)[] | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments {
warehouse: OrderDraftFinalize_draftOrderComplete_order_fulfillments_warehouse | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDraftFinalize_draftOrderComplete_order_lines_variant_preorder | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_events {
lines: (OrderDraftUpdate_draftOrderUpdate_order_events_lines | null)[] | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments {
warehouse: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_warehouse | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderDraftUpdate_draftOrderUpdate_order_lines_variant_preorder | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_lines_unitDiscount {

View file

@ -33,6 +33,11 @@ export interface OrderFulfillData_order_lines_allocations {
warehouse: OrderFulfillData_order_lines_allocations_warehouse;
}
export interface OrderFulfillData_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillData_order_lines_variant_attributes_values {
__typename: "AttributeValue";
id: string;
@ -63,6 +68,7 @@ export interface OrderFulfillData_order_lines_variant {
id: string;
name: string;
sku: string | null;
preorder: OrderFulfillData_order_lines_variant_preorder | null;
attributes: OrderFulfillData_order_lines_variant_attributes[];
stocks: (OrderFulfillData_order_lines_variant_stocks | null)[] | null;
trackInventory: boolean;

View file

@ -213,10 +213,16 @@ export interface OrderFulfillmentApprove_orderFulfillmentApprove_order_events {
lines: (OrderFulfillmentApprove_orderFulfillmentApprove_order_events_lines | null)[] | null;
}
export interface OrderFulfillmentApprove_orderFulfillmentApprove_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentApprove_orderFulfillmentApprove_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentApprove_orderFulfillmentApprove_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderFulfillmentApprove_orderFulfillmentApprove_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderFulfillmentApprove_orderFulfillmentApprove_order_fulfillme
warehouse: OrderFulfillmentApprove_orderFulfillmentApprove_order_fulfillments_warehouse | null;
}
export interface OrderFulfillmentApprove_orderFulfillmentApprove_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentApprove_orderFulfillmentApprove_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentApprove_orderFulfillmentApprove_order_lines_variant_preorder | null;
}
export interface OrderFulfillmentApprove_orderFulfillmentApprove_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events {
lines: (OrderFulfillmentCancel_orderFulfillmentCancel_order_events_lines | null)[] | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillment
warehouse: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_warehouse | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_variant_preorder | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_unitDiscount {

View file

@ -16,10 +16,16 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_e
addressType: AddressTypeEnum | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_variant_preorder | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_o
lines: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_lines | null)[] | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitDiscount {
@ -405,10 +417,16 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_o
warehouse: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_warehouse | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_variant_preorder | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
lines: (OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_lines | null)[] | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
warehouse: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_warehouse | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_variant_preorder | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderLineDelete_orderLineDelete_order_events {
lines: (OrderLineDelete_orderLineDelete_order_events_lines | null)[] | null;
}
export interface OrderLineDelete_orderLineDelete_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineDelete_orderLineDelete_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineDelete_orderLineDelete_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderLineDelete_orderLineDelete_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderLineDelete_orderLineDelete_order_fulfillments {
warehouse: OrderLineDelete_orderLineDelete_order_fulfillments_warehouse | null;
}
export interface OrderLineDelete_orderLineDelete_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineDelete_orderLineDelete_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineDelete_orderLineDelete_order_lines_variant_preorder | null;
}
export interface OrderLineDelete_orderLineDelete_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_events {
lines: (OrderLineDiscountRemove_orderLineDiscountRemove_order_events_lines | null)[] | null;
}
export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineDiscountRemove_orderLineDiscountRemove_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_fulfillme
warehouse: OrderLineDiscountRemove_orderLineDiscountRemove_order_fulfillments_warehouse | null;
}
export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineDiscountRemove_orderLineDiscountRemove_order_lines_variant_preorder | null;
}
export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_events {
lines: (OrderLineDiscountUpdate_orderLineDiscountUpdate_order_events_lines | null)[] | null;
}
export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineDiscountUpdate_orderLineDiscountUpdate_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_fulfillme
warehouse: OrderLineDiscountUpdate_orderLineDiscountUpdate_order_fulfillments_warehouse | null;
}
export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineDiscountUpdate_orderLineDiscountUpdate_order_lines_variant_preorder | null;
}
export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderLineUpdate_orderLineUpdate_order_events {
lines: (OrderLineUpdate_orderLineUpdate_order_events_lines | null)[] | null;
}
export interface OrderLineUpdate_orderLineUpdate_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineUpdate_orderLineUpdate_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineUpdate_orderLineUpdate_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderLineUpdate_orderLineUpdate_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderLineUpdate_orderLineUpdate_order_fulfillments {
warehouse: OrderLineUpdate_orderLineUpdate_order_fulfillments_warehouse | null;
}
export interface OrderLineUpdate_orderLineUpdate_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLineUpdate_orderLineUpdate_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLineUpdate_orderLineUpdate_order_lines_variant_preorder | null;
}
export interface OrderLineUpdate_orderLineUpdate_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderLinesAdd_orderLinesCreate_order_events {
lines: (OrderLinesAdd_orderLinesCreate_order_events_lines | null)[] | null;
}
export interface OrderLinesAdd_orderLinesCreate_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLinesAdd_orderLinesCreate_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLinesAdd_orderLinesCreate_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderLinesAdd_orderLinesCreate_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderLinesAdd_orderLinesCreate_order_fulfillments {
warehouse: OrderLinesAdd_orderLinesCreate_order_fulfillments_warehouse | null;
}
export interface OrderLinesAdd_orderLinesCreate_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderLinesAdd_orderLinesCreate_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderLinesAdd_orderLinesCreate_order_lines_variant_preorder | null;
}
export interface OrderLinesAdd_orderLinesCreate_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_events {
lines: (OrderMarkAsPaid_orderMarkAsPaid_order_events_lines | null)[] | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments {
warehouse: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_warehouse | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderMarkAsPaid_orderMarkAsPaid_order_lines_variant_preorder | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderRefund_orderRefund_order_events {
lines: (OrderRefund_orderRefund_order_events_lines | null)[] | null;
}
export interface OrderRefund_orderRefund_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderRefund_orderRefund_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderRefund_orderRefund_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderRefund_orderRefund_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderRefund_orderRefund_order_fulfillments {
warehouse: OrderRefund_orderRefund_order_fulfillments_warehouse | null;
}
export interface OrderRefund_orderRefund_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderRefund_orderRefund_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderRefund_orderRefund_order_lines_variant_preorder | null;
}
export interface OrderRefund_orderRefund_order_lines_unitDiscount {

View file

@ -275,10 +275,16 @@ export interface OrderShippingMethodUpdate_orderUpdateShipping_order_events {
lines: (OrderShippingMethodUpdate_orderUpdateShipping_order_events_lines | null)[] | null;
}
export interface OrderShippingMethodUpdate_orderUpdateShipping_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderShippingMethodUpdate_orderUpdateShipping_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderShippingMethodUpdate_orderUpdateShipping_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderShippingMethodUpdate_orderUpdateShipping_order_fulfillments_lines_orderLine_unitDiscount {
@ -371,10 +377,16 @@ export interface OrderShippingMethodUpdate_orderUpdateShipping_order_fulfillment
warehouse: OrderShippingMethodUpdate_orderUpdateShipping_order_fulfillments_warehouse | null;
}
export interface OrderShippingMethodUpdate_orderUpdateShipping_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderShippingMethodUpdate_orderUpdateShipping_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderShippingMethodUpdate_orderUpdateShipping_order_lines_variant_preorder | null;
}
export interface OrderShippingMethodUpdate_orderUpdateShipping_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderUpdate_orderUpdate_order_events {
lines: (OrderUpdate_orderUpdate_order_events_lines | null)[] | null;
}
export interface OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderUpdate_orderUpdate_order_fulfillments {
warehouse: OrderUpdate_orderUpdate_order_fulfillments_warehouse | null;
}
export interface OrderUpdate_orderUpdate_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderUpdate_orderUpdate_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderUpdate_orderUpdate_order_lines_variant_preorder | null;
}
export interface OrderUpdate_orderUpdate_order_lines_unitDiscount {

View file

@ -213,10 +213,16 @@ export interface OrderVoid_orderVoid_order_events {
lines: (OrderVoid_orderVoid_order_events_lines | null)[] | null;
}
export interface OrderVoid_orderVoid_order_fulfillments_lines_orderLine_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderVoid_orderVoid_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderVoid_orderVoid_order_fulfillments_lines_orderLine_variant_preorder | null;
}
export interface OrderVoid_orderVoid_order_fulfillments_lines_orderLine_unitDiscount {
@ -309,10 +315,16 @@ export interface OrderVoid_orderVoid_order_fulfillments {
warehouse: OrderVoid_orderVoid_order_fulfillments_warehouse | null;
}
export interface OrderVoid_orderVoid_order_lines_variant_preorder {
__typename: "PreorderData";
endDate: any | null;
}
export interface OrderVoid_orderVoid_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
preorder: OrderVoid_orderVoid_order_lines_variant_preorder | null;
}
export interface OrderVoid_orderVoid_order_lines_unitDiscount {

View file

@ -531,6 +531,7 @@ describe("Get the total value of all replaced products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -586,6 +587,7 @@ describe("Get the total value of all replaced products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -641,6 +643,7 @@ describe("Get the total value of all replaced products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "T-shirt",
@ -702,6 +705,7 @@ describe("Get the total value of all replaced products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -762,6 +766,7 @@ describe("Get the total value of all replaced products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -822,6 +827,7 @@ describe("Get the total value of all replaced products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "T-shirt",
@ -882,6 +888,7 @@ describe("Get the total value of all replaced products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -942,6 +949,7 @@ describe("Get the total value of all replaced products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -1136,6 +1144,7 @@ describe("Get the total value of all selected products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -1191,6 +1200,7 @@ describe("Get the total value of all selected products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -1246,6 +1256,7 @@ describe("Get the total value of all selected products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "T-shirt",
@ -1307,6 +1318,7 @@ describe("Get the total value of all selected products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -1367,6 +1379,7 @@ describe("Get the total value of all selected products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -1427,6 +1440,7 @@ describe("Get the total value of all selected products", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "T-shirt",
@ -1615,6 +1629,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -1675,6 +1690,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
@ -1735,6 +1751,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
quantityAvailable: 50,
preorder: null,
__typename: "ProductVariant"
},
productName: "T-shirt",

View file

@ -203,6 +203,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
{({
change,
data,
formErrors,
disabled: formDisabled,
handlers,
hasChanged,
@ -269,9 +270,11 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
hasVariants={false}
onFormDataChange={change}
errors={errors}
formErrors={formErrors}
stocks={data.stocks}
warehouses={warehouses}
onChange={handlers.changeStock}
onChangePreorderEndDate={handlers.changePreorderEndDate}
onWarehouseStockAdd={handlers.addStock}
onWarehouseStockDelete={handlers.deleteStock}
onWarehouseConfigure={onWarehouseConfigure}

View file

@ -18,11 +18,12 @@ import { MetadataFormData } from "@saleor/components/Metadata";
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
import { RichTextEditorChange } from "@saleor/components/RichTextEditor";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import useForm, { FormChange } from "@saleor/hooks/useForm";
import useForm, { FormChange, FormErrors } from "@saleor/hooks/useForm";
import useFormset, {
FormsetChange,
FormsetData
} from "@saleor/hooks/useFormset";
import { errorMessages } from "@saleor/intl";
import { ProductType_productType } from "@saleor/products/types/ProductType";
import {
getAttributeInputFromProductType,
@ -47,7 +48,9 @@ import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/single
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import useRichText from "@saleor/utils/richText/useRichText";
import React from "react";
import { useIntl } from "react-intl";
import { createPreorderEndDateChangeHandler } from "../../utils/handlers";
import { ProductStockFormsetData, ProductStockInput } from "../ProductStocks";
export interface ProductCreateFormData extends MetadataFormData {
@ -68,6 +71,11 @@ export interface ProductCreateFormData extends MetadataFormData {
stockQuantity: number;
taxCode: string;
trackInventory: boolean;
isPreorder: boolean;
globalThreshold: number;
globalSoldUnits: number;
hasPreorderEndDate: boolean;
preorderEndDateTime: string;
weight: string;
}
export interface ProductCreateData extends ProductCreateFormData {
@ -102,12 +110,14 @@ export interface ProductCreateHandlers
Record<"reorderAttributeValue", FormsetChange<ReorderEvent>>,
Record<"addStock" | "deleteStock", (id: string) => void> {
changeDescription: RichTextEditorChange;
changePreorderEndDate: FormChange;
fetchReferences: (value: string) => void;
fetchMoreReferences: FetchMoreProps;
}
export interface UseProductCreateFormResult {
change: FormChange;
data: ProductCreateData;
formErrors: FormErrors<ProductCreateData>;
disabled: boolean;
handlers: ProductCreateHandlers;
hasChanged: boolean;
@ -151,6 +161,7 @@ function useProductCreateForm(
onSubmit: (data: ProductCreateData) => Promise<boolean>,
opts: UseProductCreateFormOpts
): UseProductCreateFormResult {
const intl = useIntl();
const defaultInitialFormData: ProductCreateFormData &
Record<"productType", string> = {
category: "",
@ -172,7 +183,12 @@ function useProductCreateForm(
stockQuantity: null,
taxCode: null,
trackInventory: false,
weight: ""
weight: "",
globalSoldUnits: 0,
globalThreshold: 0,
isPreorder: false,
hasPreorderEndDate: false,
preorderEndDateTime: ""
};
const [changed, setChanged] = React.useState(false);
const triggerChange = () => setChanged(true);
@ -289,6 +305,12 @@ function useProductCreateForm(
triggerChange
);
const handlePreorderEndDateChange = createPreorderEndDateChangeHandler(
form,
triggerChange,
intl.formatMessage(errorMessages.preorderEndDateInFutureErrorText)
);
const getData = (): ProductCreateData => ({
...form.data,
attributes: getAttributesDisplayData(
@ -306,17 +328,21 @@ function useProductCreateForm(
const submit = () => onSubmit(data);
const disabled =
!opts.selectedProductType?.hasVariants &&
(!opts.selectedProductType?.hasVariants &&
(data.channelListings.some(
channel =>
validatePrice(channel.price) || validateCostPrice(channel.costPrice)
) ||
!data.category);
!data.category)) ||
(data.isPreorder &&
data.hasPreorderEndDate &&
!!form.errors.preorderEndDateTime);
return {
change: handleChange,
data,
disabled,
formErrors: form.errors,
handlers: {
addStock: handleStockAdd,
changeChannelPrice: handleChannelPriceChange,
@ -324,6 +350,7 @@ function useProductCreateForm(
changeDescription,
changeMetadata,
changeStock: handleStockChange,
changePreorderEndDate: handlePreorderEndDateChange,
deleteStock: handleStockDelete,
fetchMoreReferences: handleFetchMoreReferences,
fetchReferences: handleFetchReferences,

View file

@ -1,4 +1,5 @@
import {
Button,
Card,
CardContent,
ClickAwayListener,
@ -18,14 +19,19 @@ import {
import { fade } from "@material-ui/core/styles/colorManipulator";
import AddIcon from "@material-ui/icons/Add";
import DeleteIcon from "@material-ui/icons/Delete";
import {
ChannelData,
ChannelPriceAndPreorderArgs
} from "@saleor/channels/utils";
import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { DateTimeTimezoneField } from "@saleor/components/DateTimeTimezoneField";
import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr";
import Link from "@saleor/components/Link";
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment";
import { FormChange } from "@saleor/hooks/useForm";
import { FormChange, FormErrors } from "@saleor/hooks/useForm";
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
import { makeStyles } from "@saleor/macaw-ui";
import { ICONBUTTON_SIZE } from "@saleor/macaw-ui";
@ -35,6 +41,11 @@ import createNonNegativeValueChangeHandler from "@saleor/utils/handlers/nonNegat
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { ProductCreateData } from "../ProductCreatePage";
import { ProductUpdateSubmitData } from "../ProductUpdatePage/form";
import { ProductVariantCreateData } from "../ProductVariantCreatePage/form";
import { ProductVariantUpdateData } from "../ProductVariantPage/form";
export interface ProductStockFormsetData {
quantityAllocated: number;
}
@ -45,16 +56,33 @@ export type ProductStockInput = FormsetAtomicData<
export interface ProductStockFormData {
sku: string;
trackInventory: boolean;
isPreorder: boolean;
globalThreshold: number;
globalSoldUnits: number;
hasPreorderEndDate: boolean;
preorderEndDateTime?: string;
}
export interface ProductStocksProps {
productVariantChannelListings?: ChannelData[];
data: ProductStockFormData;
disabled: boolean;
errors: ProductErrorFragment[];
formErrors:
| FormErrors<ProductVariantCreateData>
| FormErrors<ProductVariantUpdateData>
| FormErrors<ProductUpdateSubmitData>
| FormErrors<ProductCreateData>;
hasVariants: boolean;
stocks: ProductStockInput[];
warehouses: WarehouseFragment[];
onVariantChannelListingChange?: (
id: string,
data: Partial<ChannelPriceAndPreorderArgs>
) => void;
onChange: FormsetChange;
onChangePreorderEndDate: FormChange;
onEndPreorderTrigger?: () => void;
onFormDataChange: FormChange;
onWarehouseStockAdd: (warehouseId: string) => void;
onWarehouseStockDelete: (warehouseId: string) => void;
@ -72,6 +100,14 @@ const useStyles = makeStyles(
textAlign: "right",
width: 150
},
colSoldUnits: {
textAlign: "right",
width: 150
},
colThreshold: {
textAlign: "right",
width: 180
},
editWarehouses: {
marginRight: theme.spacing(-1)
},
@ -107,6 +143,35 @@ const useStyles = makeStyles(
display: "grid",
gridColumnGap: theme.spacing(3),
gridTemplateColumns: "repeat(2, 1fr)"
},
dateTimeInputs: {
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2)
},
preorderInfo: {
marginBottom: theme.spacing(2),
marginTop: theme.spacing(2),
display: "block"
},
caption: {
fontSize: 14
},
thresholdRow: {
display: "grid",
gridColumnGap: theme.spacing(3),
gridTemplateColumns: "3fr 1fr",
marginTop: theme.spacing(1)
},
thresholdInput: {
maxWidth: 400
},
preorderItemsLeftCount: {
fontSize: 14,
paddingTop: theme.spacing(2),
textAlign: "center"
},
preorderLimitInfo: {
marginTop: theme.spacing(3)
}
}),
{
@ -119,10 +184,15 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
disabled,
hasVariants,
errors,
formErrors: localFormErrors,
onChangePreorderEndDate,
stocks,
warehouses,
productVariantChannelListings = [],
onChange,
onEndPreorderTrigger,
onFormDataChange,
onVariantChannelListingChange,
onWarehouseStockAdd,
onWarehouseStockDelete,
onWarehouseConfigure
@ -131,6 +201,7 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
const intl = useIntl();
const anchor = React.useRef<HTMLDivElement>();
const [isExpanded, setExpansionState] = React.useState(false);
const unitsLeft = data.globalThreshold - data.globalSoldUnits;
const warehousesToAssign =
warehouses?.filter(
@ -138,6 +209,10 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
) || [];
const formErrors = getFormErrors(["sku"], errors);
const onThresholdChange = createNonNegativeValueChangeHandler(
onFormDataChange
);
return (
<Card>
<CardTitle
@ -162,6 +237,25 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
value={data.sku}
/>
</div>
<ControlledCheckbox
checked={data.isPreorder}
name="isPreorder"
onChange={
onEndPreorderTrigger && data.isPreorder
? onEndPreorderTrigger
: onFormDataChange
}
disabled={disabled}
label={
<FormattedMessage
defaultMessage="Variant currently in preorder"
description="product inventory, checkbox"
/>
}
/>
{!data.isPreorder && (
<>
<FormSpacer />
<ControlledCheckbox
checked={data.trackInventory}
@ -180,8 +274,11 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
</>
}
/>
</>
)}
</CardContent>
<Hr />
{!data.isPreorder && (
<CardContent className={classes.quantityContainer}>
<Typography>
<div className={classes.quantityHeader}>
@ -193,8 +290,12 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
</span>
</div>
</Typography>
{!warehouses?.length && (
<Typography color="textSecondary" className={classes.noWarehouseInfo}>
<Typography
color="textSecondary"
className={classes.noWarehouseInfo}
>
{hasVariants ? (
<>
<FormattedMessage
@ -225,7 +326,8 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
</Typography>
)}
</CardContent>
{warehouses?.length > 0 && (
)}
{warehouses?.length > 0 && !data.isPreorder && (
<Table>
<colgroup>
<col className={classes.colName} />
@ -354,6 +456,181 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
</TableBody>
</Table>
)}
{data.isPreorder && (
<CardContent>
<Typography variant="caption" className={classes.caption}>
{intl.formatMessage({
defaultMessage:
"Set up an end date of preorder. When end date will be reached product will be automatically taken from preorder to standard selling",
description: "info text"
})}
</Typography>
{data.hasPreorderEndDate && (
<div className={classes.dateTimeInputs}>
<DateTimeTimezoneField
name={"preorderEndDateTime"}
disabled={disabled}
futureDatesOnly
fullWidth={false}
error={localFormErrors.preorderEndDateTime}
value={data?.preorderEndDateTime}
onChange={event =>
onChangePreorderEndDate({
target: {
name: "preorderEndDateTime",
value: event
}
})
}
/>
</div>
)}
<Button
name="hasPreorderEndDate"
color="primary"
variant="text"
disabled={disabled}
onClick={() =>
onFormDataChange({
target: {
name: "hasPreorderEndDate",
value: !data.hasPreorderEndDate
}
})
}
>
{data.hasPreorderEndDate
? intl.formatMessage({ defaultMessage: "CANCEL END DATE" })
: intl.formatMessage({ defaultMessage: "SETUP END DATE" })}
</Button>
<Typography variant="caption" className={classes.preorderLimitInfo}>
{intl.formatMessage({
defaultMessage:
"Preordered products will be available in all warehouses. You can set a threshold for sold quantity. Leaving input blank will be interpreted as no limit to sale. Sold items will be allocated at the warehouse assigned to chosen shipping zone.",
description: "info text"
})}
</Typography>
<div className={classes.thresholdRow}>
<TextField
inputProps={{
min: 0
}}
disabled={disabled}
fullWidth
helperText={intl.formatMessage({
defaultMessage:
"Threshold that cannot be exceeded even if per channel thresholds are still available"
})}
label={intl.formatMessage({
defaultMessage: "Global threshold"
})}
name="globalThreshold"
required
onChange={onThresholdChange}
value={data.globalThreshold ?? ""}
className={classes.thresholdInput}
/>
{productVariantChannelListings?.length > 0 && (
<Typography
variant="caption"
className={classes.preorderItemsLeftCount}
>
{data.globalThreshold
? intl.formatMessage(
{
defaultMessage: "{unitsLeft} units left",
description: "app has been installed"
},
{ unitsLeft }
)
: intl.formatMessage({
defaultMessage: "Unlimited",
id: "unlimitedUnitsLeft",
description: "section header"
})}
</Typography>
)}
</div>
</CardContent>
)}
{productVariantChannelListings?.length > 0 && data.isPreorder && (
<Table>
<colgroup>
<col className={classes.colName} />
<col className={classes.colSoldUnits} />
<col className={classes.colThreshold} />
</colgroup>
<TableHead>
<TableRow>
<TableCell className={classes.colName}>
<FormattedMessage
defaultMessage="Channels"
description="tabel column header"
/>
</TableCell>
<TableCell className={classes.colSoldUnits}>
<FormattedMessage
defaultMessage="Sold units"
description="table column header, sold units preorder quantity"
id="tableColSoldUnits"
/>
</TableCell>
<TableCell className={classes.colThreshold}>
<FormattedMessage
defaultMessage="Channel threshold"
description="table column header"
id="tableColChannelThreshold"
/>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{renderCollection(productVariantChannelListings, listing => {
if (!listing) {
return;
}
return (
<TableRow key={listing.id}>
<TableCell className={classes.colName}>
{listing.name}
</TableCell>
<TableCell className={classes.colQuantity}>
{listing?.unitsSold || 0}
</TableCell>
<TableCell className={classes.colQuantity}>
<TextField
disabled={disabled}
fullWidth
inputProps={{
className: classes.input,
min: 0,
type: "number"
}}
placeholder={intl.formatMessage({
defaultMessage: "Unlimited"
})}
onChange={e => {
onVariantChannelListingChange(listing.id, {
costPrice: listing.costPrice,
price: listing.price,
preorderThreshold:
e.target.value === ""
? undefined
: Number(e.target.value)
});
}}
value={listing?.preorderThreshold ?? ""}
/>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
)}
</Card>
);
};

View file

@ -286,6 +286,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
{({
change,
data,
formErrors,
disabled: formDisabled,
handlers,
hasChanged,
@ -380,14 +381,20 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
/>
<CardSpacer />
<ProductStocks
onVariantChannelListingChange={
handlers.changeChannelPreorder
}
productVariantChannelListings={data.channelListings}
data={data}
disabled={disabled}
hasVariants={false}
errors={errors}
formErrors={formErrors}
stocks={data.stocks}
warehouses={warehouses}
onChange={handlers.changeStock}
onFormDataChange={change}
onChangePreorderEndDate={handlers.changePreorderEndDate}
onWarehouseStockAdd={handlers.addStock}
onWarehouseStockDelete={handlers.deleteStock}
onWarehouseConfigure={onWarehouseConfigure}

View file

@ -9,18 +9,27 @@ import {
createFetchMoreReferencesHandler,
createFetchReferencesHandler
} from "@saleor/attributes/utils/handlers";
import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils";
import {
ChannelData,
ChannelPreorderArgs,
ChannelPriceArgs
} from "@saleor/channels/utils";
import { AttributeInput } from "@saleor/components/Attributes";
import { MetadataFormData } from "@saleor/components/Metadata";
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
import { RichTextEditorChange } from "@saleor/components/RichTextEditor";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm";
import useForm, {
FormChange,
FormErrors,
SubmitPromise
} from "@saleor/hooks/useForm";
import useFormset, {
FormsetAtomicData,
FormsetChange,
FormsetData
} from "@saleor/hooks/useFormset";
import { errorMessages } from "@saleor/intl";
import { ProductDetails_product } from "@saleor/products/types/ProductDetails";
import {
getAttributeInputFromProduct,
@ -29,7 +38,9 @@ import {
} from "@saleor/products/utils/data";
import {
createChannelsChangeHandler,
createChannelsPriceChangeHandler
createChannelsPreorderChangeHandler,
createChannelsPriceChangeHandler,
createPreorderEndDateChangeHandler
} from "@saleor/products/utils/handlers";
import {
validateCostPrice,
@ -48,6 +59,7 @@ import getMetadata from "@saleor/utils/metadata/getMetadata";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import useRichText from "@saleor/utils/richText/useRichText";
import React from "react";
import { useIntl } from "react-intl";
import { ProductStockFormsetData, ProductStockInput } from "../ProductStocks";
@ -68,6 +80,11 @@ export interface ProductUpdateFormData extends MetadataFormData {
sku: string;
taxCode: string;
trackInventory: boolean;
isPreorder: boolean;
globalThreshold: number;
globalSoldUnits: number;
hasPreorderEndDate: boolean;
preorderEndDateTime?: string;
weight: string;
}
export interface FileAttributeInputData {
@ -110,6 +127,10 @@ export interface ProductUpdateHandlers
FormsetChange<string>
>,
Record<"changeChannelPrice", (id: string, data: ChannelPriceArgs) => void>,
Record<
"changeChannelPreorder",
(id: string, data: ChannelPreorderArgs) => void
>,
Record<
"changeChannels",
(
@ -122,13 +143,14 @@ export interface ProductUpdateHandlers
Record<"reorderAttributeValue", FormsetChange<ReorderEvent>>,
Record<"addStock" | "deleteStock", (id: string) => void> {
changeDescription: RichTextEditorChange;
changePreorderEndDate: FormChange;
fetchReferences: (value: string) => void;
fetchMoreReferences: FetchMoreProps;
}
export interface UseProductUpdateFormResult {
change: FormChange;
data: ProductUpdateData;
formErrors: FormErrors<ProductUpdateSubmitData>;
disabled: boolean;
handlers: ProductUpdateHandlers;
hasChanged: boolean;
@ -198,6 +220,7 @@ function useProductUpdateForm(
onSubmit: (data: ProductUpdateSubmitData) => SubmitPromise,
opts: UseProductUpdateFormOpts
): UseProductUpdateFormResult {
const intl = useIntl();
const [changed, setChanged] = React.useState(false);
const triggerChange = () => setChanged(true);
@ -308,12 +331,24 @@ function useProductUpdateForm(
triggerChange
);
const handleChannelPreorderChange = createChannelsPreorderChangeHandler(
opts.isSimpleProduct ? opts.currentChannels : opts.channelsData,
opts.isSimpleProduct ? opts.setChannels : opts.setChannelsData,
triggerChange
);
const handleChannelPriceChange = createChannelsPriceChangeHandler(
opts.isSimpleProduct ? opts.currentChannels : opts.channelsData,
opts.isSimpleProduct ? opts.setChannels : opts.setChannelsData,
triggerChange
);
const handlePreorderEndDateChange = createPreorderEndDateChangeHandler(
form,
triggerChange,
intl.formatMessage(errorMessages.preorderEndDateInFutureErrorText)
);
const data: ProductUpdateData = {
...form.data,
channelListings: opts.currentChannels,
@ -352,23 +387,29 @@ function useProductUpdateForm(
handleFormSubmit(getSubmitData(), handleSubmit, setChanged);
const disabled =
!opts.hasVariants &&
(!opts.hasVariants &&
data.channelListings.some(
channel =>
validatePrice(channel.price) || validateCostPrice(channel.costPrice)
);
)) ||
(data.isPreorder &&
data.hasPreorderEndDate &&
!!form.errors.preorderEndDateTime);
return {
change: handleChange,
data,
disabled,
formErrors: form.errors,
handlers: {
addStock: handleStockAdd,
changeChannelPrice: handleChannelPriceChange,
changeChannelPreorder: handleChannelPreorderChange,
changeChannels: handleChannelsChange,
changeDescription,
changeMetadata,
changeStock: handleStockChange,
changePreorderEndDate: handlePreorderEndDateChange,
deleteStock: handleStockDelete,
fetchMoreReferences: handleFetchMoreReferences,
fetchReferences: handleFetchReferences,

View file

@ -2,7 +2,6 @@ import {
getAttributeValuesFromReferences,
mergeAttributeValues
} from "@saleor/attributes/utils/data";
import { ChannelPriceData } from "@saleor/channels/utils";
import AssignAttributeValueDialog from "@saleor/components/AssignAttributeValueDialog";
import Attributes, {
AttributeInput,
@ -60,7 +59,6 @@ const messages = defineMessages({
});
interface ProductVariantCreatePageProps {
channels: ChannelPriceData[];
disabled: boolean;
errors: ProductErrorWithAttributesFragment[];
header: string;
@ -89,7 +87,6 @@ interface ProductVariantCreatePageProps {
}
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
channels,
disabled,
errors,
header,
@ -141,7 +138,6 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
product={product}
onSubmit={onSubmit}
warehouses={warehouses}
currentChannels={channels}
referencePages={referencePages}
referenceProducts={referenceProducts}
fetchReferencePages={fetchReferencePages}
@ -153,6 +149,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
{({
change,
data,
formErrors,
disabled: formDisabled,
handlers,
hasChanged,
@ -236,10 +233,12 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
disabled={disabled}
hasVariants={true}
onFormDataChange={change}
formErrors={formErrors}
errors={errors}
stocks={data.stocks}
warehouses={warehouses}
onChange={handlers.changeStock}
onChangePreorderEndDate={handlers.changePreorderEndDate}
onWarehouseStockAdd={handlers.addStock}
onWarehouseStockDelete={handlers.deleteStock}
onWarehouseConfigure={onWarehouseConfigure}

View file

@ -8,22 +8,24 @@ import {
createFetchMoreReferencesHandler,
createFetchReferencesHandler
} from "@saleor/attributes/utils/handlers";
import { ChannelPriceData } from "@saleor/channels/utils";
import { AttributeInput } from "@saleor/components/Attributes";
import { MetadataFormData } from "@saleor/components/Metadata";
import useForm, { FormChange } from "@saleor/hooks/useForm";
import useForm, { FormChange, FormErrors } from "@saleor/hooks/useForm";
import useFormset, {
FormsetChange,
FormsetData
} from "@saleor/hooks/useFormset";
import { errorMessages } from "@saleor/intl";
import { ProductVariantCreateData_product } from "@saleor/products/types/ProductVariantCreateData";
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
import { createPreorderEndDateChangeHandler } from "@saleor/products/utils/handlers";
import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages";
import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts";
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
import { FetchMoreProps, ReorderEvent } from "@saleor/types";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import React from "react";
import { useIntl } from "react-intl";
import { ProductStockFormsetData, ProductStockInput } from "../ProductStocks";
@ -31,6 +33,11 @@ export interface ProductVariantCreateFormData extends MetadataFormData {
sku: string;
trackInventory: boolean;
weight: string;
isPreorder: boolean;
globalThreshold: number;
globalSoldUnits: number;
hasPreorderEndDate: boolean;
preorderEndDateTime?: string;
}
export interface ProductVariantCreateData extends ProductVariantCreateFormData {
attributes: AttributeInput[];
@ -40,7 +47,6 @@ export interface ProductVariantCreateData extends ProductVariantCreateFormData {
export interface UseProductVariantCreateFormOpts {
warehouses: SearchWarehouses_search_edges_node[];
currentChannels: ChannelPriceData[];
referencePages: SearchPages_search_edges_node[];
referenceProducts: SearchProducts_search_edges_node[];
fetchReferencePages?: (data: string) => void;
@ -60,6 +66,7 @@ export interface ProductVariantCreateHandlers
Record<"reorderAttributeValue", FormsetChange<ReorderEvent>>,
Record<"addStock" | "deleteStock", (id: string) => void> {
changeMetadata: FormChange;
changePreorderEndDate: FormChange;
fetchReferences: (value: string) => void;
fetchMoreReferences: FetchMoreProps;
}
@ -67,6 +74,7 @@ export interface ProductVariantCreateHandlers
export interface UseProductVariantCreateFormResult {
change: FormChange;
data: ProductVariantCreateData;
formErrors: FormErrors<ProductVariantCreateData>;
disabled: boolean;
// TODO: type FormsetChange
handlers: ProductVariantCreateHandlers;
@ -86,7 +94,12 @@ const initial: ProductVariantCreateFormData = {
privateMetadata: [],
sku: "",
trackInventory: true,
weight: ""
weight: "",
isPreorder: false,
globalThreshold: null,
globalSoldUnits: 0,
hasPreorderEndDate: false,
preorderEndDateTime: ""
};
function useProductVariantCreateForm(
@ -94,6 +107,7 @@ function useProductVariantCreateForm(
onSubmit: (data: ProductVariantCreateData) => void,
opts: UseProductVariantCreateFormOpts
): UseProductVariantCreateFormResult {
const intl = useIntl();
const [changed, setChanged] = React.useState(false);
const triggerChange = () => setChanged(true);
@ -169,6 +183,12 @@ function useProductVariantCreateForm(
stocks.remove(id);
};
const handlePreorderEndDateChange = createPreorderEndDateChangeHandler(
form,
triggerChange,
intl.formatMessage(errorMessages.preorderEndDateInFutureErrorText)
);
const data: ProductVariantCreateData = {
...form.data,
attributes: getAttributesDisplayData(
@ -186,11 +206,16 @@ function useProductVariantCreateForm(
return {
change: handleChange,
data,
disabled: false,
disabled:
data.isPreorder &&
data.hasPreorderEndDate &&
!!form.errors.preorderEndDateTime,
formErrors: form.errors,
handlers: {
addStock: handleStockAdd,
changeMetadata,
changeStock: handleStockChange,
changePreorderEndDate: handlePreorderEndDateChange,
deleteStock: handleStockDelete,
fetchMoreReferences: handleFetchMoreReferences,
fetchReferences: handleFetchReferences,

View file

@ -0,0 +1,52 @@
import { DialogContentText } from "@material-ui/core";
import ActionDialog from "@saleor/components/ActionDialog";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import React from "react";
import { useIntl } from "react-intl";
import { productVariantEndPreorderDialogMessages } from "./messages";
export interface ProductVariantEndPreorderDialogProps {
confirmButtonState: ConfirmButtonTransitionState;
open: boolean;
onClose: () => void;
onConfirm: () => void;
variantGlobalSoldUnits?: number;
}
const ProductVariantEndPreorderDialog: React.FC<ProductVariantEndPreorderDialogProps> = ({
confirmButtonState,
open,
onClose,
onConfirm,
variantGlobalSoldUnits
}) => {
const intl = useIntl();
return (
<ActionDialog
confirmButtonLabel={intl.formatMessage(
productVariantEndPreorderDialogMessages.dialogConfirmButtonLabel
)}
confirmButtonState={confirmButtonState}
open={open}
onClose={onClose}
onConfirm={onConfirm}
title={intl.formatMessage(
productVariantEndPreorderDialogMessages.dialogTitle
)}
variant="default"
>
<DialogContentText>
{intl.formatMessage(
productVariantEndPreorderDialogMessages.dialogMessage,
{
variantGlobalSoldUnits
}
)}
</DialogContentText>
</ActionDialog>
);
};
ProductVariantEndPreorderDialog.displayName = "ProductVariantEndPreorderDialog";
export default ProductVariantEndPreorderDialog;

View file

@ -0,0 +1,2 @@
export * from "./ProductVariantEndPreorderDialog";
export { default } from "./ProductVariantEndPreorderDialog";

View file

@ -0,0 +1,16 @@
import { defineMessages } from "react-intl";
export const productVariantEndPreorderDialogMessages = defineMessages({
dialogTitle: {
defaultMessage: "Ending preorder",
description: "dialog header"
},
dialogMessage: {
defaultMessage:
"You are about to end your products preorder. You have sold {variantGlobalSoldUnits} units of this variant. Sold units will be allocated at appropriate warehouses. Remember to add remaining threshold stock to warehouses."
},
dialogConfirmButtonLabel: {
defaultMessage: "ACCEPT",
description: "button label"
}
});

View file

@ -32,6 +32,7 @@ import { defineMessages, useIntl } from "react-intl";
import { maybe } from "../../../misc";
import ProductShipping from "../ProductShipping/ProductShipping";
import ProductStocks, { ProductStockInput } from "../ProductStocks";
import ProductVariantEndPreorderDialog from "../ProductVariantEndPreorderDialog";
import ProductVariantMediaSelectDialog from "../ProductVariantImageSelectDialog";
import ProductVariantMedia from "../ProductVariantMedia";
import ProductVariantNavigation from "../ProductVariantNavigation";
@ -97,6 +98,8 @@ interface ProductVariantPageProps {
fetchAttributeValues: (query: string, attributeId: string) => void;
onAssignReferencesClick: (attribute: AttributeInput) => void;
onCloseDialog: () => void;
onVariantPreorderDeactivate: (id: string) => void;
variantDeactivatePreoderButtonState: ConfirmButtonTransitionState;
onVariantReorder: ReorderAction;
onAttributeSelectBlur: () => void;
onAdd();
@ -130,6 +133,8 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
onMediaSelect,
onSubmit,
onVariantClick,
onVariantPreorderDeactivate,
variantDeactivatePreoderButtonState,
onVariantReorder,
onSetDefaultVariant,
onWarehouseConfigure,
@ -149,6 +154,11 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
const [isModalOpened, setModalStatus] = React.useState(false);
const toggleModal = () => setModalStatus(!isModalOpened);
const [
isEndPreorderModalOpened,
setIsEndPreorderModalOpened
] = React.useState(false);
const variantMedia = variant?.media?.map(image => image.id);
const productMedia = variant?.product?.media?.sort((prev, next) =>
prev.sortOrder > next.sortOrder ? 1 : -1
@ -159,6 +169,11 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
const canOpenAssignReferencesAttributeDialog = !!assignReferencesAttributeId;
const handleDeactivatePreorder = async () => {
await onVariantPreorderDeactivate(variant.id);
setIsEndPreorderModalOpened(false);
};
const handleAssignReferenceAttribute = (
attributeValues: string[],
data: ProductVariantUpdateData,
@ -202,6 +217,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
{({
change,
data,
formErrors,
disabled: formDisabled,
handlers,
hasChanged,
@ -305,14 +321,28 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
/>
<CardSpacer />
<ProductStocks
productVariantChannelListings={data.channelListings.map(
channel => ({
...channel.data,
...channel.value
})
)}
onVariantChannelListingChange={handlers.changeChannels}
data={data}
disabled={loading}
hasVariants={true}
errors={errors}
formErrors={formErrors}
stocks={data.stocks}
warehouses={warehouses}
onChange={handlers.changeStock}
onFormDataChange={change}
onChangePreorderEndDate={handlers.changePreorderEndDate}
onEndPreorderTrigger={
!!variant?.preorder
? () => setIsEndPreorderModalOpened(true)
: null
}
onWarehouseStockAdd={handlers.addStock}
onWarehouseStockDelete={handlers.deleteStock}
onWarehouseConfigure={onWarehouseConfigure}
@ -364,6 +394,15 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
selectedMedia={maybe(() => variant.media.map(image => image.id))}
/>
)}
{!!variant?.preorder && (
<ProductVariantEndPreorderDialog
confirmButtonState={variantDeactivatePreoderButtonState}
onClose={() => setIsEndPreorderModalOpened(false)}
onConfirm={handleDeactivatePreorder}
open={isEndPreorderModalOpened}
variantGlobalSoldUnits={variant?.preorder?.globalSoldUnits}
/>
)}
</>
);
};

View file

@ -8,20 +8,31 @@ import {
createFetchMoreReferencesHandler,
createFetchReferencesHandler
} from "@saleor/attributes/utils/handlers";
import { ChannelPriceData, IChannelPriceArgs } from "@saleor/channels/utils";
import {
ChannelPriceAndPreorderData,
IChannelPriceAndPreorderArgs
} from "@saleor/channels/utils";
import { AttributeInput } from "@saleor/components/Attributes";
import { MetadataFormData } from "@saleor/components/Metadata";
import { ProductVariant } from "@saleor/fragments/types/ProductVariant";
import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm";
import useForm, {
FormChange,
FormErrors,
SubmitPromise
} from "@saleor/hooks/useForm";
import useFormset, {
FormsetChange,
FormsetData
} from "@saleor/hooks/useFormset";
import { errorMessages } from "@saleor/intl";
import {
getAttributeInputFromVariant,
getStockInputFromVariant
} from "@saleor/products/utils/data";
import { getChannelsInput } from "@saleor/products/utils/handlers";
import {
createPreorderEndDateChangeHandler,
getChannelsInput
} from "@saleor/products/utils/handlers";
import {
validateCostPrice,
validatePrice
@ -35,6 +46,7 @@ import { mapMetadataItemToInput } from "@saleor/utils/maps";
import getMetadata from "@saleor/utils/metadata/getMetadata";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import React from "react";
import { useIntl } from "react-intl";
import handleFormSubmit from "../../../utils/handlers/handleFormSubmit";
import { ProductStockInput } from "../ProductStocks";
@ -43,9 +55,17 @@ export interface ProductVariantUpdateFormData extends MetadataFormData {
sku: string;
trackInventory: boolean;
weight: string;
isPreorder: boolean;
globalThreshold: number;
globalSoldUnits: number;
hasPreorderEndDate: boolean;
preorderEndDateTime?: string;
}
export interface ProductVariantUpdateData extends ProductVariantUpdateFormData {
channelListings: FormsetData<ChannelPriceData, IChannelPriceArgs>;
channelListings: FormsetData<
ChannelPriceAndPreorderData,
IChannelPriceAndPreorderArgs
>;
attributes: AttributeInput[];
stocks: ProductStockInput[];
}
@ -54,14 +74,17 @@ export interface ProductVariantUpdateSubmitData
attributes: AttributeInput[];
attributesWithNewFileValue: FormsetData<null, File>;
addStocks: ProductStockInput[];
channelListings: FormsetData<ChannelPriceData, IChannelPriceArgs>;
channelListings: FormsetData<
ChannelPriceAndPreorderData,
IChannelPriceAndPreorderArgs
>;
updateStocks: ProductStockInput[];
removeStocks: string[];
}
export interface UseProductVariantUpdateFormOpts {
warehouses: SearchWarehouses_search_edges_node[];
currentChannels: ChannelPriceData[];
currentChannels: ChannelPriceAndPreorderData[];
referencePages: SearchPages_search_edges_node[];
referenceProducts: SearchProducts_search_edges_node[];
fetchReferencePages?: (data: string) => void;
@ -83,6 +106,7 @@ export interface ProductVariantUpdateHandlers
Record<"selectAttributeFile", FormsetChange<File>>,
Record<"reorderAttributeValue", FormsetChange<ReorderEvent>>,
Record<"addStock" | "deleteStock", (id: string) => void> {
changePreorderEndDate: FormChange;
changeMetadata: FormChange;
fetchReferences: (value: string) => void;
fetchMoreReferences: FetchMoreProps;
@ -91,6 +115,7 @@ export interface ProductVariantUpdateHandlers
export interface UseProductVariantUpdateFormResult {
change: FormChange;
data: ProductVariantUpdateData;
formErrors: FormErrors<ProductVariantUpdateData>;
disabled: boolean;
handlers: ProductVariantUpdateHandlers;
hasChanged: boolean;
@ -109,18 +134,37 @@ function useProductVariantUpdateForm(
onSubmit: (data: ProductVariantUpdateSubmitData) => SubmitPromise,
opts: UseProductVariantUpdateFormOpts
): UseProductVariantUpdateFormResult {
const intl = useIntl();
const [changed, setChanged] = React.useState(false);
const triggerChange = () => setChanged(true);
const attributeInput = getAttributeInputFromVariant(variant);
const stockInput = getStockInputFromVariant(variant);
const channelsInput = getChannelsInput(opts.currentChannels);
const currentChannelsWithPreorderInfo = opts.currentChannels?.map(channel => {
const variantChannel = variant?.channelListings?.find(
channelListing => channelListing.channel.id === channel.id
);
return {
...channel,
preorderThreshold: variantChannel?.preorderThreshold?.quantity,
soldUnits: variantChannel?.preorderThreshold?.soldUnits
};
});
const channelsInput = getChannelsInput(currentChannelsWithPreorderInfo);
const initial: ProductVariantUpdateFormData = {
metadata: variant?.metadata?.map(mapMetadataItemToInput),
privateMetadata: variant?.privateMetadata?.map(mapMetadataItemToInput),
sku: variant?.sku || "",
trackInventory: variant?.trackInventory,
isPreorder: !!variant?.preorder || false,
globalThreshold: variant?.preorder?.globalThreshold || null,
globalSoldUnits: variant?.preorder?.globalSoldUnits || 0,
hasPreorderEndDate: !!variant?.preorder?.endDate,
preorderEndDateTime: variant?.preorder?.endDate,
weight: variant?.weight?.value.toString() || ""
};
@ -204,6 +248,12 @@ function useProductVariantUpdateForm(
triggerChange();
};
const handlePreorderEndDateChange = createPreorderEndDateChangeHandler(
form,
triggerChange,
intl.formatMessage(errorMessages.preorderEndDateInFutureErrorText)
);
const dataStocks = stocks.data.map(stock => stock.id);
const variantStocks = variant?.stocks.map(stock => stock.warehouse.id) || [];
const stockDiff = arrayDiff(variantStocks, dataStocks);
@ -215,11 +265,6 @@ function useProductVariantUpdateForm(
stock => !stockDiff.added.some(addedStock => addedStock === stock.id)
);
const disabled = channels?.data.some(
channelData =>
validatePrice(channelData.value.price) ||
validateCostPrice(channelData.value.costPrice)
);
const data: ProductVariantUpdateData = {
...form.data,
attributes: getAttributesDisplayData(
@ -231,6 +276,17 @@ function useProductVariantUpdateForm(
channelListings: channels.data,
stocks: stocks.data
};
const disabled =
channels?.data.some(
channelData =>
validatePrice(channelData.value.price) ||
validateCostPrice(channelData.value.costPrice)
) ||
(data.isPreorder &&
data.hasPreorderEndDate &&
!!form.errors.preorderEndDateTime);
const submitData: ProductVariantUpdateSubmitData = {
...form.data,
...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified),
@ -258,11 +314,13 @@ function useProductVariantUpdateForm(
change: handleChange,
data,
disabled,
formErrors: form.errors,
handlers: {
addStock: handleStockAdd,
changeChannels: handleChannelChange,
changeMetadata,
changeStock: handleStockChange,
changePreorderEndDate: handlePreorderEndDateChange,
deleteStock: handleStockDelete,
fetchMoreReferences: handleFetchMoreReferences,
fetchReferences: handleFetchReferences,

View file

@ -7,7 +7,11 @@ import {
TableRow,
Typography
} from "@material-ui/core";
import { ChannelData, ChannelPriceArgs } from "@saleor/channels/utils";
import {
ChannelData,
ChannelPriceAndPreorderArgs,
ChannelPriceArgs
} from "@saleor/channels/utils";
import CardTitle from "@saleor/components/CardTitle";
import PriceField from "@saleor/components/PriceField";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
@ -66,7 +70,10 @@ interface ProductVariantPriceProps {
errors?: ProductChannelListingErrorFragment[];
loading?: boolean;
disabled?: boolean;
onChange?: (id: string, data: ChannelPriceArgs) => void;
onChange?: (
id: string,
data: ChannelPriceArgs | ChannelPriceAndPreorderArgs
) => void;
disabledMessage?: MessageDescriptor;
}
@ -178,7 +185,8 @@ const ProductVariantPrice: React.FC<ProductVariantPriceProps> = props => {
onChange={e =>
onChange(listing.id, {
costPrice: listing.costPrice,
price: e.target.value
price: e.target.value,
preorderThreshold: listing.preorderThreshold
})
}
disabled={loading}
@ -207,7 +215,8 @@ const ProductVariantPrice: React.FC<ProductVariantPriceProps> = props => {
onChange={e =>
onChange(listing.id, {
costPrice: e.target.value,
price: listing.price
price: listing.price,
preorderThreshold: listing.preorderThreshold
})
}
disabled={loading}

View file

@ -631,10 +631,11 @@ export const product: (
}
],
trackInventory: true,
weight: {
__typename: "Weight",
unit: WeightUnitsEnum.KG,
value: 3
preorder: {
__typename: "PreorderData",
endDate: null,
globalSoldUnits: null,
globalThreshold: 0
}
},
{
@ -657,6 +658,11 @@ export const product: (
__typename: "Money",
amount: 1,
currency: "USD"
},
preorderThreshold: {
__typename: "PreorderThreshold",
quantity: 0,
soldUnits: 0
}
},
{
@ -676,6 +682,11 @@ export const product: (
__typename: "Money",
amount: 1,
currency: "USD"
},
preorderThreshold: {
__typename: "PreorderThreshold",
quantity: 0,
soldUnits: 0
}
}
],
@ -709,10 +720,11 @@ export const product: (
}
],
trackInventory: false,
weight: {
__typename: "Weight",
unit: WeightUnitsEnum.KG,
value: 4
preorder: {
__typename: "PreorderData",
endDate: null,
globalSoldUnits: null,
globalThreshold: 0
}
}
],
@ -2940,6 +2952,11 @@ export const variant = (placeholderImage: string): ProductVariant => ({
__typename: "Money",
amount: 10,
currency: "USD"
},
preorderThreshold: {
__typename: "PreorderThreshold",
quantity: 0,
soldUnits: 0
}
},
{
@ -2959,6 +2976,11 @@ export const variant = (placeholderImage: string): ProductVariant => ({
__typename: "Money",
amount: 20,
currency: "USD"
},
preorderThreshold: {
__typename: "PreorderThreshold",
quantity: 0,
soldUnits: 0
}
}
],
@ -3502,6 +3524,12 @@ export const variant = (placeholderImage: string): ProductVariant => ({
}
],
trackInventory: true,
preorder: {
__typename: "PreorderData",
endDate: null,
globalSoldUnits: null,
globalThreshold: 0
},
weight: {
__typename: "Weight",
unit: WeightUnitsEnum.KG,

View file

@ -11,6 +11,7 @@ import {
channelListingProductFragment,
channelListingProductVariantFragment,
exportFileFragment,
fragmentPreorder,
fragmentProductMedia,
fragmentVariant,
productFragmentDetails
@ -66,6 +67,10 @@ import {
ProductVariantChannelListingUpdate,
ProductVariantChannelListingUpdateVariables
} from "./types/ProductVariantChannelListingUpdate";
import {
ProductVariantPreorderDeactivate,
ProductVariantPreorderDeactivateVariables
} from "./types/ProductVariantPreorderDeactivate";
import {
ProductVariantReorder,
ProductVariantReorderVariables
@ -334,6 +339,7 @@ export const variantUpdateMutation = gql`
$sku: String
$trackInventory: Boolean!
$stocks: [StockInput!]!
$preorder: PreorderSettingsInput
$weight: WeightScalar
$firstValues: Int
$afterValues: String
@ -346,6 +352,7 @@ export const variantUpdateMutation = gql`
attributes: $attributes
sku: $sku
trackInventory: $trackInventory
preorder: $preorder
weight: $weight
}
) {
@ -686,3 +693,25 @@ export const useProductVariantChannelListingUpdate = makeMutation<
ProductVariantChannelListingUpdate,
ProductVariantChannelListingUpdateVariables
>(ProductVariantChannelListingUpdateMutation);
export const ProductVariantPreorderDeactivateMutation = gql`
${fragmentPreorder}
${productErrorFragment}
mutation ProductVariantPreorderDeactivate($id: ID!) {
productVariantPreorderDeactivate(id: $id) {
productVariant {
id
preorder {
...PreorderFragment
}
}
errors {
...ProductErrorFragment
}
}
}
`;
export const useProductVariantPreorderDeactivateMutation = makeMutation<
ProductVariantPreorderDeactivate,
ProductVariantPreorderDeactivateVariables
>(ProductVariantPreorderDeactivateMutation);

View file

@ -79,11 +79,18 @@ export interface ProductChannelListingUpdate_productChannelListingUpdate_product
currency: string;
}
export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings {
__typename: "ProductVariantChannelListing";
channel: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_channel;
price: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_price | null;
costPrice: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_costPrice | null;
preorderThreshold: ProductChannelListingUpdate_productChannelListingUpdate_product_variants_channelListings_preorderThreshold | null;
}
export interface ProductChannelListingUpdate_productChannelListingUpdate_product_variants {

View file

@ -260,6 +260,13 @@ export interface ProductDetails_product_variants_stocks {
warehouse: ProductDetails_product_variants_stocks_warehouse;
}
export interface ProductDetails_product_variants_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface ProductDetails_product_variants_channelListings_channel {
__typename: "Channel";
id: string;
@ -279,11 +286,18 @@ export interface ProductDetails_product_variants_channelListings_costPrice {
currency: string;
}
export interface ProductDetails_product_variants_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface ProductDetails_product_variants_channelListings {
__typename: "ProductVariantChannelListing";
channel: ProductDetails_product_variants_channelListings_channel;
price: ProductDetails_product_variants_channelListings_price | null;
costPrice: ProductDetails_product_variants_channelListings_costPrice | null;
preorderThreshold: ProductDetails_product_variants_channelListings_preorderThreshold | null;
}
export interface ProductDetails_product_variants {
@ -295,6 +309,7 @@ export interface ProductDetails_product_variants {
media: ProductDetails_product_variants_media[] | null;
stocks: (ProductDetails_product_variants_stocks | null)[] | null;
trackInventory: boolean;
preorder: ProductDetails_product_variants_preorder | null;
channelListings: ProductDetails_product_variants_channelListings[] | null;
}

View file

@ -267,6 +267,13 @@ export interface ProductUpdate_productUpdate_product_variants_stocks {
warehouse: ProductUpdate_productUpdate_product_variants_stocks_warehouse;
}
export interface ProductUpdate_productUpdate_product_variants_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface ProductUpdate_productUpdate_product_variants_channelListings_channel {
__typename: "Channel";
id: string;
@ -286,11 +293,18 @@ export interface ProductUpdate_productUpdate_product_variants_channelListings_co
currency: string;
}
export interface ProductUpdate_productUpdate_product_variants_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface ProductUpdate_productUpdate_product_variants_channelListings {
__typename: "ProductVariantChannelListing";
channel: ProductUpdate_productUpdate_product_variants_channelListings_channel;
price: ProductUpdate_productUpdate_product_variants_channelListings_price | null;
costPrice: ProductUpdate_productUpdate_product_variants_channelListings_costPrice | null;
preorderThreshold: ProductUpdate_productUpdate_product_variants_channelListings_preorderThreshold | null;
}
export interface ProductUpdate_productUpdate_product_variants {
@ -302,6 +316,7 @@ export interface ProductUpdate_productUpdate_product_variants {
media: ProductUpdate_productUpdate_product_variants_media[] | null;
stocks: (ProductUpdate_productUpdate_product_variants_stocks | null)[] | null;
trackInventory: boolean;
preorder: ProductUpdate_productUpdate_product_variants_preorder | null;
channelListings: ProductUpdate_productUpdate_product_variants_channelListings[] | null;
}

View file

@ -28,11 +28,18 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing
currency: string;
}
export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings {
__typename: "ProductVariantChannelListing";
channel: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_channel;
price: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_price | null;
costPrice: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_costPrice | null;
preorderThreshold: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_channelListings_preorderThreshold | null;
}
export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings_channel {

View file

@ -299,11 +299,18 @@ export interface ProductVariantDetails_productVariant_channelListings_costPrice
currency: string;
}
export interface ProductVariantDetails_productVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface ProductVariantDetails_productVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: ProductVariantDetails_productVariant_channelListings_channel;
price: ProductVariantDetails_productVariant_channelListings_price | null;
costPrice: ProductVariantDetails_productVariant_channelListings_costPrice | null;
preorderThreshold: ProductVariantDetails_productVariant_channelListings_preorderThreshold | null;
}
export interface ProductVariantDetails_productVariant_stocks_warehouse {
@ -320,6 +327,13 @@ export interface ProductVariantDetails_productVariant_stocks {
warehouse: ProductVariantDetails_productVariant_stocks_warehouse;
}
export interface ProductVariantDetails_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface ProductVariantDetails_productVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -340,6 +354,7 @@ export interface ProductVariantDetails_productVariant {
sku: string | null;
stocks: (ProductVariantDetails_productVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: ProductVariantDetails_productVariant_preorder | null;
weight: ProductVariantDetails_productVariant_weight | null;
}

View file

@ -0,0 +1,43 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: ProductVariantPreorderDeactivate
// ====================================================
export interface ProductVariantPreorderDeactivate_productVariantPreorderDeactivate_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface ProductVariantPreorderDeactivate_productVariantPreorderDeactivate_productVariant {
__typename: "ProductVariant";
id: string;
preorder: ProductVariantPreorderDeactivate_productVariantPreorderDeactivate_productVariant_preorder | null;
}
export interface ProductVariantPreorderDeactivate_productVariantPreorderDeactivate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface ProductVariantPreorderDeactivate_productVariantPreorderDeactivate {
__typename: "ProductVariantPreorderDeactivate";
productVariant: ProductVariantPreorderDeactivate_productVariantPreorderDeactivate_productVariant | null;
errors: ProductVariantPreorderDeactivate_productVariantPreorderDeactivate_errors[];
}
export interface ProductVariantPreorderDeactivate {
productVariantPreorderDeactivate: ProductVariantPreorderDeactivate_productVariantPreorderDeactivate | null;
}
export interface ProductVariantPreorderDeactivateVariables {
id: string;
}

View file

@ -267,6 +267,13 @@ export interface SimpleProductUpdate_productUpdate_product_variants_stocks {
warehouse: SimpleProductUpdate_productUpdate_product_variants_stocks_warehouse;
}
export interface SimpleProductUpdate_productUpdate_product_variants_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface SimpleProductUpdate_productUpdate_product_variants_channelListings_channel {
__typename: "Channel";
id: string;
@ -286,11 +293,18 @@ export interface SimpleProductUpdate_productUpdate_product_variants_channelListi
currency: string;
}
export interface SimpleProductUpdate_productUpdate_product_variants_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface SimpleProductUpdate_productUpdate_product_variants_channelListings {
__typename: "ProductVariantChannelListing";
channel: SimpleProductUpdate_productUpdate_product_variants_channelListings_channel;
price: SimpleProductUpdate_productUpdate_product_variants_channelListings_price | null;
costPrice: SimpleProductUpdate_productUpdate_product_variants_channelListings_costPrice | null;
preorderThreshold: SimpleProductUpdate_productUpdate_product_variants_channelListings_preorderThreshold | null;
}
export interface SimpleProductUpdate_productUpdate_product_variants {
@ -302,6 +316,7 @@ export interface SimpleProductUpdate_productUpdate_product_variants {
media: SimpleProductUpdate_productUpdate_product_variants_media[] | null;
stocks: (SimpleProductUpdate_productUpdate_product_variants_stocks | null)[] | null;
trackInventory: boolean;
preorder: SimpleProductUpdate_productUpdate_product_variants_preorder | null;
channelListings: SimpleProductUpdate_productUpdate_product_variants_channelListings[] | null;
}
@ -645,11 +660,18 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_channel
currency: string;
}
export interface SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface SimpleProductUpdate_productVariantUpdate_productVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_channel;
price: SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_price | null;
costPrice: SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_costPrice | null;
preorderThreshold: SimpleProductUpdate_productVariantUpdate_productVariant_channelListings_preorderThreshold | null;
}
export interface SimpleProductUpdate_productVariantUpdate_productVariant_stocks_warehouse {
@ -666,6 +688,13 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_stocks
warehouse: SimpleProductUpdate_productVariantUpdate_productVariant_stocks_warehouse;
}
export interface SimpleProductUpdate_productVariantUpdate_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface SimpleProductUpdate_productVariantUpdate_productVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -686,6 +715,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant {
sku: string | null;
stocks: (SimpleProductUpdate_productVariantUpdate_productVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: SimpleProductUpdate_productVariantUpdate_productVariant_preorder | null;
weight: SimpleProductUpdate_productVariantUpdate_productVariant_weight | null;
}
@ -992,11 +1022,18 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_c
currency: string;
}
export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_channel;
price: SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_price | null;
costPrice: SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_costPrice | null;
preorderThreshold: SimpleProductUpdate_productVariantStocksCreate_productVariant_channelListings_preorderThreshold | null;
}
export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks_warehouse {
@ -1013,6 +1050,13 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_s
warehouse: SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks_warehouse;
}
export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -1033,6 +1077,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant {
sku: string | null;
stocks: (SimpleProductUpdate_productVariantStocksCreate_productVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: SimpleProductUpdate_productVariantStocksCreate_productVariant_preorder | null;
weight: SimpleProductUpdate_productVariantStocksCreate_productVariant_weight | null;
}
@ -1338,11 +1383,18 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_c
currency: string;
}
export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_channel;
price: SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_price | null;
costPrice: SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_costPrice | null;
preorderThreshold: SimpleProductUpdate_productVariantStocksDelete_productVariant_channelListings_preorderThreshold | null;
}
export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks_warehouse {
@ -1359,6 +1411,13 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_s
warehouse: SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks_warehouse;
}
export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -1379,6 +1438,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant {
sku: string | null;
stocks: (SimpleProductUpdate_productVariantStocksDelete_productVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: SimpleProductUpdate_productVariantStocksDelete_productVariant_preorder | null;
weight: SimpleProductUpdate_productVariantStocksDelete_productVariant_weight | null;
}
@ -1685,11 +1745,18 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_c
currency: string;
}
export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_channel;
price: SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_price | null;
costPrice: SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_costPrice | null;
preorderThreshold: SimpleProductUpdate_productVariantStocksUpdate_productVariant_channelListings_preorderThreshold | null;
}
export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse {
@ -1706,6 +1773,13 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_s
warehouse: SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse;
}
export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -1726,6 +1800,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant {
sku: string | null;
stocks: (SimpleProductUpdate_productVariantStocksUpdate_productVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: SimpleProductUpdate_productVariantStocksUpdate_productVariant_preorder | null;
weight: SimpleProductUpdate_productVariantStocksUpdate_productVariant_weight | null;
}

View file

@ -306,11 +306,18 @@ export interface VariantCreate_productVariantCreate_productVariant_channelListin
currency: string;
}
export interface VariantCreate_productVariantCreate_productVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface VariantCreate_productVariantCreate_productVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: VariantCreate_productVariantCreate_productVariant_channelListings_channel;
price: VariantCreate_productVariantCreate_productVariant_channelListings_price | null;
costPrice: VariantCreate_productVariantCreate_productVariant_channelListings_costPrice | null;
preorderThreshold: VariantCreate_productVariantCreate_productVariant_channelListings_preorderThreshold | null;
}
export interface VariantCreate_productVariantCreate_productVariant_stocks_warehouse {
@ -327,6 +334,13 @@ export interface VariantCreate_productVariantCreate_productVariant_stocks {
warehouse: VariantCreate_productVariantCreate_productVariant_stocks_warehouse;
}
export interface VariantCreate_productVariantCreate_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface VariantCreate_productVariantCreate_productVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -347,6 +361,7 @@ export interface VariantCreate_productVariantCreate_productVariant {
sku: string | null;
stocks: (VariantCreate_productVariantCreate_productVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: VariantCreate_productVariantCreate_productVariant_preorder | null;
weight: VariantCreate_productVariantCreate_productVariant_weight | null;
}

View file

@ -3,7 +3,7 @@
// @generated
// This file was automatically generated and should not be edited.
import { StockInput, AttributeValueInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes";
import { StockInput, AttributeValueInput, PreorderSettingsInput, ProductErrorCode, AttributeInputTypeEnum, AttributeEntityTypeEnum, MeasurementUnitsEnum, ProductMediaType, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: VariantUpdate
@ -306,11 +306,18 @@ export interface VariantUpdate_productVariantUpdate_productVariant_channelListin
currency: string;
}
export interface VariantUpdate_productVariantUpdate_productVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface VariantUpdate_productVariantUpdate_productVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: VariantUpdate_productVariantUpdate_productVariant_channelListings_channel;
price: VariantUpdate_productVariantUpdate_productVariant_channelListings_price | null;
costPrice: VariantUpdate_productVariantUpdate_productVariant_channelListings_costPrice | null;
preorderThreshold: VariantUpdate_productVariantUpdate_productVariant_channelListings_preorderThreshold | null;
}
export interface VariantUpdate_productVariantUpdate_productVariant_stocks_warehouse {
@ -327,6 +334,13 @@ export interface VariantUpdate_productVariantUpdate_productVariant_stocks {
warehouse: VariantUpdate_productVariantUpdate_productVariant_stocks_warehouse;
}
export interface VariantUpdate_productVariantUpdate_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface VariantUpdate_productVariantUpdate_productVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -347,6 +361,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant {
sku: string | null;
stocks: (VariantUpdate_productVariantUpdate_productVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: VariantUpdate_productVariantUpdate_productVariant_preorder | null;
weight: VariantUpdate_productVariantUpdate_productVariant_weight | null;
}
@ -653,11 +668,18 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_channel
currency: string;
}
export interface VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_preorderThreshold {
__typename: "PreorderThreshold";
quantity: number | null;
soldUnits: number;
}
export interface VariantUpdate_productVariantStocksUpdate_productVariant_channelListings {
__typename: "ProductVariantChannelListing";
channel: VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_channel;
price: VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_price | null;
costPrice: VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_costPrice | null;
preorderThreshold: VariantUpdate_productVariantStocksUpdate_productVariant_channelListings_preorderThreshold | null;
}
export interface VariantUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse {
@ -674,6 +696,13 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_stocks
warehouse: VariantUpdate_productVariantStocksUpdate_productVariant_stocks_warehouse;
}
export interface VariantUpdate_productVariantStocksUpdate_productVariant_preorder {
__typename: "PreorderData";
globalThreshold: number | null;
globalSoldUnits: number;
endDate: any | null;
}
export interface VariantUpdate_productVariantStocksUpdate_productVariant_weight {
__typename: "Weight";
unit: WeightUnitsEnum;
@ -694,6 +723,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant {
sku: string | null;
stocks: (VariantUpdate_productVariantStocksUpdate_productVariant_stocks | null)[] | null;
trackInventory: boolean;
preorder: VariantUpdate_productVariantStocksUpdate_productVariant_preorder | null;
weight: VariantUpdate_productVariantStocksUpdate_productVariant_weight | null;
}
@ -783,6 +813,7 @@ export interface VariantUpdateVariables {
sku?: string | null;
trackInventory: boolean;
stocks: StockInput[];
preorder?: PreorderSettingsInput | null;
weight?: any | null;
firstValues?: number | null;
afterValues?: string | null;

View file

@ -21,6 +21,7 @@ import {
} from "@saleor/products/types/ProductDetails";
import { StockInput } from "@saleor/types/globalTypes";
import { mapEdgesToItems, mapMetadataItemToInput } from "@saleor/utils/maps";
import moment from "moment";
import { ProductStockInput } from "../components/ProductStocks";
import { ProductType_productType_productAttributes } from "../types/ProductType";
@ -225,6 +226,11 @@ export interface ProductUpdatePageFormData extends MetadataFormData {
taxCode: string;
trackInventory: boolean;
weight: string;
isPreorder: boolean;
globalThreshold: number;
globalSoldUnits: number;
hasPreorderEndDate: boolean;
preorderEndDateTime?: string;
}
export function getProductUpdatePageFormData(
@ -234,6 +240,7 @@ export function getProductUpdatePageFormData(
channelsData: ChannelData[],
channelsWithVariants: ChannelsWithVariantsData
): ProductUpdatePageFormData {
const variant = product?.variants[0];
return {
channelsWithVariants,
channelsData,
@ -244,7 +251,7 @@ export function getProductUpdatePageFormData(
() => product.collections.map(collection => collection.id),
[]
),
channelListings: currentChannels,
channelListings: currentChannels.map(listing => ({ ...listing })),
isAvailable: !!product?.isAvailable,
metadata: product?.metadata?.map(mapMetadataItemToInput),
name: maybe(() => product.name, ""),
@ -263,8 +270,13 @@ export function getProductUpdatePageFormData(
),
slug: product?.slug || "",
taxCode: product?.taxType.taxCode,
trackInventory: !!product?.variants[0]?.trackInventory,
weight: product?.weight?.value.toString() || ""
trackInventory: !!variant?.trackInventory,
weight: product?.weight?.value.toString() || "",
isPreorder: !!variant?.preorder || false,
globalThreshold: variant?.preorder?.globalThreshold || 0,
globalSoldUnits: variant?.preorder?.globalSoldUnits || 0,
hasPreorderEndDate: !!variant?.preorder?.endDate,
preorderEndDateTime: variant?.preorder?.endDate
};
}
@ -276,3 +288,9 @@ export function mapFormsetStockToStockInput(
warehouse: stock.id
};
}
export const getPreorderEndDateFormData = (endDate?: string) =>
endDate ? moment(endDate).format("YYYY-MM-DD") : "";
export const getPreorderEndHourFormData = (endDate?: string) =>
endDate ? moment(endDate).format("HH:mm") : "";

View file

@ -1,9 +1,12 @@
import {
ChannelData,
ChannelPreorderArgs,
ChannelPriceAndPreorderData,
ChannelPriceArgs,
ChannelPriceData
} from "@saleor/channels/utils";
import { FormChange } from "@saleor/hooks/useForm";
import { FormChange, UseFormResult } from "@saleor/hooks/useForm";
import moment from "moment";
export function createChannelsPriceChangeHandler(
channelListings: ChannelData[],
@ -12,20 +15,29 @@ export function createChannelsPriceChangeHandler(
) {
return (id: string, priceData: ChannelPriceArgs) => {
const { costPrice, price } = priceData;
const channelIndex = channelListings.findIndex(
channel => channel.id === id
);
const channel = channelListings[channelIndex];
const updatedChannels = [
...channelListings.slice(0, channelIndex),
{
...channel,
costPrice,
price
},
...channelListings.slice(channelIndex + 1)
];
const updatedChannels = channelListings.map(channel =>
channel.id === id ? { ...channel, costPrice, price } : channel
);
updateChannels(updatedChannels);
triggerChange();
};
}
export function createChannelsPreorderChangeHandler(
channelListings: ChannelData[],
updateChannels: (data: ChannelData[]) => void,
triggerChange: () => void
) {
return (id: string, preorderData: ChannelPreorderArgs) => {
const { preorderThreshold, unitsSold } = preorderData;
const updatedChannels = channelListings.map(channel =>
channel.id === id ? { ...channel, preorderThreshold, unitsSold } : channel
);
updateChannels(updatedChannels);
triggerChange();
@ -96,14 +108,15 @@ export function createProductTypeSelectHandler(
};
}
export const getChannelsInput = (channels: ChannelPriceData[]) =>
export const getChannelsInput = (channels: ChannelPriceAndPreorderData[]) =>
channels?.map(channel => ({
data: channel,
id: channel.id,
label: channel.name,
value: {
costPrice: channel.costPrice || "",
price: channel.price || ""
price: channel.price || "",
preorderThreshold: channel.preorderThreshold || null
}
}));
@ -127,3 +140,17 @@ export const getAvailabilityVariables = (channels: ChannelData[]) =>
visibleInListings: channel.visibleInListings
};
});
export const createPreorderEndDateChangeHandler = (
form: UseFormResult<{ preorderEndDateTime?: string }>,
triggerChange: () => void,
preorderPastDateErrorMessage: string
): FormChange => (event, cb) => {
form.change(event, cb);
if (moment(event.target.value).isSameOrBefore(Date.now())) {
form.setError("preorderEndDateTime", preorderPastDateErrorMessage);
} else {
form.clearErrors("preorderEndDateTime");
}
triggerChange();
};

View file

@ -36,7 +36,11 @@ export const getSimpleProductVariables = (
productVariantId: productId,
productVariantInput: {
sku: data.sku,
trackInventory: data.trackInventory
trackInventory: data.trackInventory,
preorder: {
globalThreshold: data.globalThreshold,
endDate: data.preorderEndDateTime
}
},
updateStocks: data.updateStocks.map(mapFormsetStockToStockInput)
});
@ -188,5 +192,6 @@ export const getVariantChannelsInput = ({
channelListings.map(listing => ({
channelId: listing.id,
costPrice: listing.costPrice || null,
price: listing.price
price: listing.price,
preorderThreshold: listing.preorderThreshold
}));

View file

@ -21,7 +21,10 @@ import useNotifier from "@saleor/hooks/useNotifier";
import useOnSetDefaultVariant from "@saleor/hooks/useOnSetDefaultVariant";
import useShop from "@saleor/hooks/useShop";
import { commonMessages } from "@saleor/intl";
import { useProductVariantChannelListingUpdate } from "@saleor/products/mutations";
import {
useProductVariantChannelListingUpdate,
useProductVariantPreorderDeactivateMutation
} from "@saleor/products/mutations";
import { ProductVariantDetails_productVariant } from "@saleor/products/types/ProductVariantDetails";
import usePageSearch from "@saleor/searches/usePageSearch";
import useProductSearch from "@saleor/searches/useProductSearch";
@ -155,23 +158,36 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
data: ProductVariantUpdateSubmitData,
variant: ProductVariantDetails_productVariant
) => {
const isChannelPriceChange = data.channelListings.some(channel => {
const channelsHaveChanged = data.channelListings.some(channel => {
const variantChannel = variant.channelListings.find(
variantChannel => variantChannel.channel.id === channel.id
);
const priceHasChanged =
channel.value.price !== variantChannel?.price?.amount.toString();
const costPriceHasChanged =
channel.value.costPrice !==
variantChannel?.costPrice?.amount.toString();
const preorderThresholdHasChanged =
channel.value?.preorderThreshold !==
variantChannel.preorderThreshold.quantity;
return (
channel.value.price !== variantChannel?.price?.amount.toString() ||
channel.value.costPrice !== variantChannel?.costPrice?.amount.toString()
priceHasChanged || costPriceHasChanged || preorderThresholdHasChanged
);
});
if (isChannelPriceChange) {
if (channelsHaveChanged) {
await updateChannels({
variables: {
id: variant.id,
input: data.channelListings.map(listing => ({
channelId: listing.id,
costPrice: listing.value.costPrice || null,
price: listing.value.price
price: listing.value.price,
preorderThreshold: listing.value.preorderThreshold
}))
}
});
@ -185,6 +201,13 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
return <NotFoundPage onBack={handleBack} />;
}
const [
deactivatePreorder,
deactivatePreoderOpts
] = useProductVariantPreorderDeactivateMutation({});
const handleDeactivateVariantPreorder = async (id: string) =>
deactivatePreorder({ variables: { id } });
const [
reorderProductVariants,
reorderProductVariantsOpts
@ -204,6 +227,7 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
updateVariantOpts.loading ||
assignMediaOpts.loading ||
unassignMediaOpts.loading ||
deactivatePreoderOpts.loading ||
reorderProductVariantsOpts.loading ||
deleteAttributeValueOpts.loading;
@ -256,6 +280,12 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
sku: data.sku,
stocks: data.updateStocks.map(mapFormsetStockToStockInput),
trackInventory: data.trackInventory,
preorder: data.isPreorder
? {
globalThreshold: data.globalThreshold,
endDate: data?.preorderEndDateTime || null
}
: null,
weight: weight(data.weight),
firstValues: 10
}
@ -359,6 +389,8 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
onVariantClick={variantId => {
navigate(productVariantEditUrl(productId, variantId));
}}
onVariantPreorderDeactivate={handleDeactivateVariantPreorder}
variantDeactivatePreoderButtonState={deactivatePreoderOpts.status}
onVariantReorder={handleVariantReorder}
assignReferencesAttributeId={
params.action === "assign-attribute-value" && params.id

View file

@ -3,7 +3,6 @@ import {
handleUploadMultipleFiles,
prepareAttributesInput
} from "@saleor/attributes/utils/handlers";
import { ChannelPriceData } from "@saleor/channels/utils";
import { AttributeInput } from "@saleor/components/Attributes";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { WindowTitle } from "@saleor/components/WindowTitle";
@ -73,16 +72,6 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
const product = data?.product;
const channels: ChannelPriceData[] = product?.channelListings.map(
listing => ({
costPrice: null,
currency: listing.channel.currencyCode,
id: listing.channel.id,
name: listing.channel.name,
price: null
})
);
const [variantCreate, variantCreateResult] = useVariantCreateMutation({});
const [updateMetadata] = useMetadataUpdate({});
@ -129,7 +118,11 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
warehouse: stock.id
})),
trackInventory: true,
weight: weight(formData.weight)
weight: weight(formData.weight),
preorder: {
globalThreshold: formData.globalThreshold,
endDate: formData.preorderEndDateTime
}
},
firstValues: 10
}
@ -210,7 +203,6 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
})}
/>
<ProductVariantCreatePage
channels={channels}
disabled={disableForm}
errors={variantCreateResult.data?.productVariantCreate.errors || []}
header={intl.formatMessage({

View file

@ -36,7 +36,7 @@ export interface SearchProducts_search_edges_node_variants {
__typename: "ProductVariant";
id: string;
name: string;
sku: string;
sku: string | null;
channelListings: SearchProducts_search_edges_node_variants_channelListings[] | null;
}

File diff suppressed because it is too large Load diff

View file

@ -9,19 +9,11 @@ import { product as productFixture } from "../../../products/fixtures";
import Decorator from "../../Decorator";
const product = productFixture(placeholderImage);
const channels = product.channelListings.map(listing => ({
costPrice: null,
currency: listing.channel.currencyCode,
id: listing.channel.id,
name: listing.channel.name,
price: null
}));
storiesOf("Views / Products / Create product variant", module)
.addDecorator(Decorator)
.add("default", () => (
<ProductVariantCreatePage
channels={channels}
weightUnit="kg"
disabled={false}
errors={[]}
@ -45,7 +37,6 @@ storiesOf("Views / Products / Create product variant", module)
))
.add("with errors", () => (
<ProductVariantCreatePage
channels={channels}
weightUnit="kg"
disabled={false}
errors={[
@ -88,7 +79,6 @@ storiesOf("Views / Products / Create product variant", module)
))
.add("when loading data", () => (
<ProductVariantCreatePage
channels={channels}
weightUnit="kg"
disabled={true}
errors={[]}
@ -112,7 +102,6 @@ storiesOf("Views / Products / Create product variant", module)
))
.add("add first variant", () => (
<ProductVariantCreatePage
channels={channels}
weightUnit="kg"
disabled={false}
errors={[]}
@ -139,7 +128,6 @@ storiesOf("Views / Products / Create product variant", module)
))
.add("no warehouses", () => (
<ProductVariantCreatePage
channels={channels}
weightUnit="kg"
disabled={false}
errors={[]}

View file

@ -31,6 +31,7 @@ storiesOf("Views / Products / Product variant details", module)
onVariantClick={() => undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
variantDeactivatePreoderButtonState="default"
warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
referencePages={[]}
@ -40,6 +41,7 @@ storiesOf("Views / Products / Product variant details", module)
onAssignReferencesClick={() => undefined}
onCloseDialog={() => undefined}
onAttributeSelectBlur={() => undefined}
onVariantPreorderDeactivate={() => undefined}
/>
))
.add("when loading data", () => (
@ -60,6 +62,7 @@ storiesOf("Views / Products / Product variant details", module)
onVariantClick={() => undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
variantDeactivatePreoderButtonState="default"
warehouses={warehouseList}
onWarehouseConfigure={() => undefined}
referencePages={[]}
@ -69,6 +72,7 @@ storiesOf("Views / Products / Product variant details", module)
onAssignReferencesClick={() => undefined}
onCloseDialog={() => undefined}
onAttributeSelectBlur={() => undefined}
onVariantPreorderDeactivate={() => undefined}
/>
))
.add("no warehouses", () => (
@ -88,6 +92,7 @@ storiesOf("Views / Products / Product variant details", module)
onVariantClick={() => undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
variantDeactivatePreoderButtonState="default"
warehouses={[]}
onWarehouseConfigure={() => undefined}
referencePages={[]}
@ -97,6 +102,7 @@ storiesOf("Views / Products / Product variant details", module)
onAssignReferencesClick={() => undefined}
onCloseDialog={() => undefined}
onAttributeSelectBlur={() => undefined}
onVariantPreorderDeactivate={() => undefined}
/>
))
.add("attribute errors", () => (
@ -114,6 +120,7 @@ storiesOf("Views / Products / Product variant details", module)
onVariantClick={() => undefined}
onVariantReorder={() => undefined}
saveButtonBarState="default"
variantDeactivatePreoderButtonState="default"
errors={[
{
attributes: [variant.selectionAttributes[0].attribute.id],
@ -153,5 +160,6 @@ storiesOf("Views / Products / Product variant details", module)
onAssignReferencesClick={() => undefined}
onCloseDialog={() => undefined}
onAttributeSelectBlur={() => undefined}
onVariantPreorderDeactivate={() => undefined}
/>
));

View file

@ -1628,6 +1628,7 @@ export enum ProductErrorCode {
NOT_FOUND = "NOT_FOUND",
NOT_PRODUCTS_IMAGE = "NOT_PRODUCTS_IMAGE",
NOT_PRODUCTS_VARIANT = "NOT_PRODUCTS_VARIANT",
PREORDER_VARIANT_CANNOT_BE_DEACTIVATED = "PREORDER_VARIANT_CANNOT_BE_DEACTIVATED",
PRODUCT_NOT_ASSIGNED_TO_CHANNEL = "PRODUCT_NOT_ASSIGNED_TO_CHANNEL",
PRODUCT_WITHOUT_CATEGORY = "PRODUCT_WITHOUT_CATEGORY",
REQUIRED = "REQUIRED",
@ -2497,6 +2498,11 @@ export interface PluginUpdateInput {
configuration?: (ConfigurationItemInput | null)[] | null;
}
export interface PreorderSettingsInput {
globalThreshold?: number | null;
endDate?: any | null;
}
export interface PriceInput {
currency: string;
amount: any;
@ -2558,6 +2564,7 @@ export interface ProductFilterInput {
productTypes?: (string | null)[] | null;
giftCard?: boolean | null;
ids?: (string | null)[] | null;
hasPreorderedVariants?: boolean | null;
channel?: string | null;
}
@ -2619,6 +2626,7 @@ export interface ProductVariantBulkCreateInput {
sku?: string | null;
trackInventory?: boolean | null;
weight?: any | null;
preorder?: PreorderSettingsInput | null;
stocks?: StockInput[] | null;
channelListings?: ProductVariantChannelListingAddInput[] | null;
}
@ -2627,6 +2635,7 @@ export interface ProductVariantChannelListingAddInput {
channelId: string;
price: any;
costPrice?: any | null;
preorderThreshold?: number | null;
}
export interface ProductVariantCreateInput {
@ -2634,6 +2643,7 @@ export interface ProductVariantCreateInput {
sku?: string | null;
trackInventory?: boolean | null;
weight?: any | null;
preorder?: PreorderSettingsInput | null;
product: string;
stocks?: StockInput[] | null;
}
@ -2643,6 +2653,7 @@ export interface ProductVariantInput {
sku?: string | null;
trackInventory?: boolean | null;
weight?: any | null;
preorder?: PreorderSettingsInput | null;
}
export interface PublishableChannelListingInput {