Attach metadata to the fulfillments within the orders (#3667)
* Metadata for fulfillment * Metadata for fulfillment * Trigger deploy * Fix removing priv metadata * Remove blinks * tests for adding, deleteing and updating public and prvate metadata for fullfilled orders (#3684) --------- Co-authored-by: wojteknowacki <124166231+wojteknowacki@users.noreply.github.com> Co-authored-by: wojteknowacki <wojciech.nowacki@saleor.io>
This commit is contained in:
parent
ed8b75a9b3
commit
730c96db88
21 changed files with 693 additions and 143 deletions
|
@ -9,41 +9,43 @@ import {
|
||||||
ORDERS_SELECTORS,
|
ORDERS_SELECTORS,
|
||||||
SHARED_ELEMENTS,
|
SHARED_ELEMENTS,
|
||||||
} from "../../elements/";
|
} from "../../elements/";
|
||||||
import { MESSAGES } from "../../fixtures";
|
import { MESSAGES, ONE_PERMISSION_USERS, urlList } from "../../fixtures";
|
||||||
import { urlList } from "../../fixtures/urlList";
|
|
||||||
import { ONE_PERMISSION_USERS } from "../../fixtures/users";
|
|
||||||
import {
|
import {
|
||||||
createCustomer,
|
createCustomer,
|
||||||
deleteCustomersStartsWith,
|
deleteCustomersStartsWith,
|
||||||
} from "../../support/api/requests/Customer";
|
|
||||||
import {
|
|
||||||
getOrder,
|
getOrder,
|
||||||
|
updateMetadata,
|
||||||
updateOrdersSettings,
|
updateOrdersSettings,
|
||||||
} from "../../support/api/requests/Order";
|
updatePrivateMetadata,
|
||||||
import { getDefaultChannel } from "../../support/api/utils/channelsUtils";
|
} from "../../support/api/requests/";
|
||||||
import {
|
import {
|
||||||
createFulfilledOrder,
|
createFulfilledOrder,
|
||||||
createOrder,
|
createOrder,
|
||||||
createReadyToFulfillOrder,
|
createReadyToFulfillOrder,
|
||||||
createUnconfirmedOrder,
|
|
||||||
} from "../../support/api/utils/ordersUtils";
|
|
||||||
import * as productsUtils from "../../support/api/utils/products/productsUtils";
|
|
||||||
import {
|
|
||||||
createShipping,
|
createShipping,
|
||||||
|
createUnconfirmedOrder,
|
||||||
deleteShippingStartsWith,
|
deleteShippingStartsWith,
|
||||||
} from "../../support/api/utils/shippingUtils";
|
getDefaultChannel,
|
||||||
import {
|
|
||||||
getDefaultTaxClass,
|
getDefaultTaxClass,
|
||||||
|
productsUtils,
|
||||||
updateTaxConfigurationForChannel,
|
updateTaxConfigurationForChannel,
|
||||||
} from "../../support/api/utils/taxesUtils";
|
} from "../../support/api/utils/";
|
||||||
import { selectChannelInPicker } from "../../support/pages/channelsPage";
|
|
||||||
import { finalizeDraftOrder } from "../../support/pages/draftOrderPage";
|
|
||||||
import {
|
import {
|
||||||
addNewProductToOrder,
|
addNewProductToOrder,
|
||||||
|
addPrivateMetadataFieldFulfillmentOrder,
|
||||||
|
addPublicMetadataFieldFulfillmentOrder,
|
||||||
applyFixedLineDiscountForProduct,
|
applyFixedLineDiscountForProduct,
|
||||||
changeQuantityOfProducts,
|
changeQuantityOfProducts,
|
||||||
|
deletePrivateFulfillmentMetadata,
|
||||||
deleteProductFromGridTableOnIndex,
|
deleteProductFromGridTableOnIndex,
|
||||||
} from "../../support/pages/ordersOperations";
|
deletePublicFulfillmentMetadata,
|
||||||
|
expandPrivateFulfillmentMetadata,
|
||||||
|
expandPublicFulfillmentMetadata,
|
||||||
|
finalizeDraftOrder,
|
||||||
|
selectChannelInPicker,
|
||||||
|
updatePrivateMetadataFieldFulfillmentOrder,
|
||||||
|
updatePublicMetadataFieldFulfillmentOrder,
|
||||||
|
} from "../../support/pages/";
|
||||||
|
|
||||||
describe("Orders", () => {
|
describe("Orders", () => {
|
||||||
const startsWith = "CyOrders-";
|
const startsWith = "CyOrders-";
|
||||||
|
@ -59,6 +61,16 @@ describe("Orders", () => {
|
||||||
|
|
||||||
const shippingPrice = 2;
|
const shippingPrice = 2;
|
||||||
const variantPrice = 1;
|
const variantPrice = 1;
|
||||||
|
const metadataName = randomName + "- metadata name";
|
||||||
|
const metadataValue = randomName + "- metadata value";
|
||||||
|
const privateMetadataName = randomName + "- private metadata name";
|
||||||
|
const privateMetadataValue = randomName + "- private metadata value";
|
||||||
|
const updatedMetadataName = metadataName + "- updated metadata name";
|
||||||
|
const updatedMetadataValue = metadataValue + "- updated metadata value";
|
||||||
|
const updatedPrivateMetadataName =
|
||||||
|
privateMetadataName + "- updated private metadata name";
|
||||||
|
const updatedPrivateMetadataValue =
|
||||||
|
privateMetadataValue + "- updated private metadata value";
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.clearSessionData().loginUserViaRequest();
|
cy.clearSessionData().loginUserViaRequest();
|
||||||
|
@ -335,4 +347,181 @@ describe("Orders", () => {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
"should create metadata and private metadata for fulfilled order . TC: SALEOR_2129",
|
||||||
|
{ tags: ["@orders", "@allEnv", "@stable"] },
|
||||||
|
() => {
|
||||||
|
let order;
|
||||||
|
|
||||||
|
cy.addAliasToGraphRequest("UpdateMetadata");
|
||||||
|
cy.addAliasToGraphRequest("UpdatePrivateMetadata");
|
||||||
|
createFulfilledOrder({
|
||||||
|
customerId: customer.id,
|
||||||
|
channelId: defaultChannel.id,
|
||||||
|
shippingMethod,
|
||||||
|
variantsList,
|
||||||
|
address,
|
||||||
|
warehouse: warehouse.id,
|
||||||
|
})
|
||||||
|
.then(({ order: orderResp }) => {
|
||||||
|
order = orderResp;
|
||||||
|
cy.visit(urlList.orders + `${order.id}`);
|
||||||
|
addPublicMetadataFieldFulfillmentOrder(
|
||||||
|
0,
|
||||||
|
metadataName,
|
||||||
|
metadataValue,
|
||||||
|
);
|
||||||
|
addPrivateMetadataFieldFulfillmentOrder(
|
||||||
|
0,
|
||||||
|
privateMetadataName,
|
||||||
|
privateMetadataValue,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
cy.clickConfirmButton()
|
||||||
|
.waitForRequestAndCheckIfNoErrors("@UpdateMetadata")
|
||||||
|
.waitForRequestAndCheckIfNoErrors("@UpdatePrivateMetadata");
|
||||||
|
cy.confirmationMessageShouldAppear();
|
||||||
|
getOrder(order.id).then(orderResponse => {
|
||||||
|
// check to see updated fulfillment metadata and private meta data
|
||||||
|
cy.wrap(orderResponse.fulfillments[0].metadata[0]).should(
|
||||||
|
"deep.equal",
|
||||||
|
{
|
||||||
|
key: metadataName,
|
||||||
|
value: metadataValue,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cy.wrap(orderResponse.fulfillments[0].privateMetadata[0]).should(
|
||||||
|
"deep.equal",
|
||||||
|
{
|
||||||
|
key: privateMetadataName,
|
||||||
|
value: privateMetadataValue,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
it(
|
||||||
|
"should update metadata and private metadata for fulfilled order . TC: SALEOR_2130",
|
||||||
|
{ tags: ["@orders", "@allEnv", "@stable"] },
|
||||||
|
() => {
|
||||||
|
cy.addAliasToGraphRequest("UpdateMetadata");
|
||||||
|
cy.addAliasToGraphRequest("UpdatePrivateMetadata");
|
||||||
|
createFulfilledOrder({
|
||||||
|
customerId: customer.id,
|
||||||
|
channelId: defaultChannel.id,
|
||||||
|
shippingMethod,
|
||||||
|
variantsList,
|
||||||
|
address,
|
||||||
|
warehouse: warehouse.id,
|
||||||
|
}).then(orderResp => {
|
||||||
|
getOrder(orderResp.order.id).then(orderDetails => {
|
||||||
|
updateMetadata(
|
||||||
|
orderDetails.fulfillments[0].id,
|
||||||
|
metadataName,
|
||||||
|
metadataValue,
|
||||||
|
).then(() => {
|
||||||
|
updatePrivateMetadata(
|
||||||
|
orderDetails.fulfillments[0].id,
|
||||||
|
privateMetadataName,
|
||||||
|
privateMetadataValue,
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
cy.visit(urlList.orders + `${orderResp.order.id}`);
|
||||||
|
updatePublicMetadataFieldFulfillmentOrder(
|
||||||
|
0,
|
||||||
|
updatedMetadataName,
|
||||||
|
updatedMetadataValue,
|
||||||
|
);
|
||||||
|
updatePrivateMetadataFieldFulfillmentOrder(
|
||||||
|
0,
|
||||||
|
updatedPrivateMetadataName,
|
||||||
|
updatedPrivateMetadataValue,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
cy.clickConfirmButton()
|
||||||
|
.waitForRequestAndCheckIfNoErrors("@UpdateMetadata")
|
||||||
|
.waitForRequestAndCheckIfNoErrors("@UpdatePrivateMetadata");
|
||||||
|
cy.confirmationMessageShouldAppear();
|
||||||
|
getOrder(orderResp.order.id).then(orderResponse => {
|
||||||
|
// check to see updated fulfillment metadata and private meta data
|
||||||
|
cy.wrap(orderResponse.fulfillments[0].metadata[0]).should(
|
||||||
|
"deep.equal",
|
||||||
|
{
|
||||||
|
key: updatedMetadataName,
|
||||||
|
value: updatedMetadataValue,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cy.wrap(
|
||||||
|
orderResponse.fulfillments[0].privateMetadata[0],
|
||||||
|
).should("deep.equal", {
|
||||||
|
key: updatedPrivateMetadataName,
|
||||||
|
value: updatedPrivateMetadataValue,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
it(
|
||||||
|
"should delete metadata and private metadata for fulfilled order . TC: SALEOR_2131",
|
||||||
|
{ tags: ["@orders", "@allEnv", "@stable"] },
|
||||||
|
() => {
|
||||||
|
cy.addAliasToGraphRequest("UpdateMetadata");
|
||||||
|
cy.addAliasToGraphRequest("UpdatePrivateMetadata");
|
||||||
|
createFulfilledOrder({
|
||||||
|
customerId: customer.id,
|
||||||
|
channelId: defaultChannel.id,
|
||||||
|
shippingMethod,
|
||||||
|
variantsList,
|
||||||
|
address,
|
||||||
|
warehouse: warehouse.id,
|
||||||
|
}).then(orderResp => {
|
||||||
|
getOrder(orderResp.order.id).then(orderDetails => {
|
||||||
|
updateMetadata(
|
||||||
|
orderDetails.fulfillments[0].id,
|
||||||
|
metadataName,
|
||||||
|
metadataValue,
|
||||||
|
).then(() => {
|
||||||
|
updatePrivateMetadata(
|
||||||
|
orderDetails.fulfillments[0].id,
|
||||||
|
privateMetadataName,
|
||||||
|
privateMetadataValue,
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
cy.visit(urlList.orders + `${orderResp.order.id}`);
|
||||||
|
expandPublicFulfillmentMetadata(0);
|
||||||
|
deletePublicFulfillmentMetadata(0, 0);
|
||||||
|
expandPrivateFulfillmentMetadata(0);
|
||||||
|
deletePrivateFulfillmentMetadata(0, 0);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
cy.clickConfirmButton()
|
||||||
|
.waitForRequestAndCheckIfNoErrors("@UpdateMetadata")
|
||||||
|
.waitForRequestAndCheckIfNoErrors("@UpdatePrivateMetadata");
|
||||||
|
cy.confirmationMessageShouldAppear();
|
||||||
|
cy.contains(privateMetadataName).should("not.exist");
|
||||||
|
cy.contains(metadataName).should("not.exist");
|
||||||
|
getOrder(orderResp.order.id).then(orderResponse => {
|
||||||
|
// check to see updated fulfillment metadata and private meta data
|
||||||
|
cy.wrap(orderResponse.fulfillments[0].metadata).should(
|
||||||
|
"deep.equal",
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
cy.wrap(orderResponse.fulfillments[0].privateMetadata).should(
|
||||||
|
"deep.equal",
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,5 +5,10 @@ export const METADATA_FORM = {
|
||||||
"[data-test-id='metadata-editor'][data-test-is-private='true']",
|
"[data-test-id='metadata-editor'][data-test-is-private='true']",
|
||||||
addFieldButton: "[data-test-id='add-field']",
|
addFieldButton: "[data-test-id='add-field']",
|
||||||
nameInput: "[name*='name']",
|
nameInput: "[name*='name']",
|
||||||
valueField: "[name*='value']"
|
valueField: "[name*='value']",
|
||||||
|
metaExpandButton: "[data-test-id='expand']",
|
||||||
|
metaDeletedButton: "[data-test-id='delete-field-0']",
|
||||||
|
privateMetaSection: "[data-test-is-private='true']",
|
||||||
|
publicMetaSection: "[data-test-is-private='false']",
|
||||||
|
fulfillmentMetaSection: "[data-test-id='fulfilled-order-section']",
|
||||||
};
|
};
|
||||||
|
|
|
@ -155,6 +155,17 @@ export function getOrder(orderId) {
|
||||||
countryArea
|
countryArea
|
||||||
phone
|
phone
|
||||||
}
|
}
|
||||||
|
fulfillments{
|
||||||
|
id
|
||||||
|
metadata{
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
privateMetadata{
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
return cy.sendRequestWithQuery(query).its("body.data.order");
|
return cy.sendRequestWithQuery(query).its("body.data.order");
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export { createChannel, updateChannelOrderSettings } from "./Channels";
|
export { createChannel, updateChannelOrderSettings } from "./Channels";
|
||||||
export { createCustomer, deleteCustomersStartsWith } from "./Customer";
|
export { createCustomer, deleteCustomersStartsWith } from "./Customer";
|
||||||
export { createDraftOrder, getOrder } from "./Order";
|
export { createDraftOrder, getOrder, updateOrdersSettings } from "./Order";
|
||||||
export { updateMetadata, updatePrivateMetadata } from "./Metadata";
|
export { updateMetadata, updatePrivateMetadata } from "./Metadata";
|
||||||
export { getProductMetadata } from "./storeFront/ProductDetails";
|
export { getProductMetadata } from "./storeFront/ProductDetails";
|
||||||
export { activatePlugin, updatePlugin } from "./Plugins";
|
export { activatePlugin, updatePlugin } from "./Plugins";
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
export { deleteChannelsStartsWith, getDefaultChannel } from "./channelsUtils";
|
export { deleteChannelsStartsWith, getDefaultChannel } from "./channelsUtils";
|
||||||
export { createOrder, createReadyToFulfillOrder } from "./ordersUtils";
|
|
||||||
export { createShipping, deleteShippingStartsWith } from "./shippingUtils";
|
export { createShipping, deleteShippingStartsWith } from "./shippingUtils";
|
||||||
export {
|
export {
|
||||||
getDefaultTaxClass,
|
getDefaultTaxClass,
|
||||||
updateTaxConfigurationForChannel,
|
updateTaxConfigurationForChannel,
|
||||||
} from "./taxesUtils";
|
} from "./taxesUtils";
|
||||||
export * as productsUtils from "./products/productsUtils";
|
export * as productsUtils from "./products/productsUtils";
|
||||||
|
|
||||||
|
export {
|
||||||
|
createFulfilledOrder,
|
||||||
|
createOrder,
|
||||||
|
createReadyToFulfillOrder,
|
||||||
|
createUnconfirmedOrder,
|
||||||
|
} from "./ordersUtils";
|
||||||
export {
|
export {
|
||||||
getMailActivationLinkForUser,
|
getMailActivationLinkForUser,
|
||||||
getMailActivationLinkForUserAndSubject,
|
getMailActivationLinkForUserAndSubject,
|
||||||
|
|
|
@ -20,6 +20,9 @@ Cypress.Commands.add("clickPrevPagePaginationButton", () =>
|
||||||
Cypress.Commands.add("clickSubmitButton", () =>
|
Cypress.Commands.add("clickSubmitButton", () =>
|
||||||
cy.get(BUTTON_SELECTORS.submit).click(),
|
cy.get(BUTTON_SELECTORS.submit).click(),
|
||||||
);
|
);
|
||||||
|
Cypress.Commands.add("clickConfirmButton", () =>
|
||||||
|
cy.get(BUTTON_SELECTORS.confirm).click(),
|
||||||
|
);
|
||||||
|
|
||||||
Cypress.Commands.add("createNewOption", (selectSelector, newOption) => {
|
Cypress.Commands.add("createNewOption", (selectSelector, newOption) => {
|
||||||
cy.get(selectSelector).type(newOption);
|
cy.get(selectSelector).type(newOption);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { METADATA_FORM } from "../../../elements/shared/metadata/metadata-form";
|
||||||
|
|
||||||
export const metadataForms = {
|
export const metadataForms = {
|
||||||
private: METADATA_FORM.privateMetadataForm,
|
private: METADATA_FORM.privateMetadataForm,
|
||||||
public: METADATA_FORM.metadataForm
|
public: METADATA_FORM.metadataForm,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function addMetadataField({ metadataForm, name, value }) {
|
export function addMetadataField({ metadataForm, name, value }) {
|
||||||
|
@ -17,3 +17,123 @@ export function addMetadataField({ metadataForm, name, value }) {
|
||||||
.find(METADATA_FORM.valueField)
|
.find(METADATA_FORM.valueField)
|
||||||
.type(value);
|
.type(value);
|
||||||
}
|
}
|
||||||
|
export function addPublicMetadataFieldFulfillmentOrder(
|
||||||
|
fulfillmentIndex,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
) {
|
||||||
|
expandPublicFulfillmentMetadata(fulfillmentIndex);
|
||||||
|
cy.get('[data-test-id="fulfilled-order-section"]')
|
||||||
|
.eq(fulfillmentIndex)
|
||||||
|
.find('[data-test-is-private="false"]')
|
||||||
|
.find(METADATA_FORM.addFieldButton)
|
||||||
|
.click();
|
||||||
|
typePublicFulfillmentMetadataName(name, 0);
|
||||||
|
typePublicFulfillmentMetadataValue(value, 0);
|
||||||
|
}
|
||||||
|
export function updatePublicMetadataFieldFulfillmentOrder(
|
||||||
|
fulfillmentIndex,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
) {
|
||||||
|
expandPublicFulfillmentMetadata(fulfillmentIndex);
|
||||||
|
typePublicFulfillmentMetadataName(name, 0);
|
||||||
|
typePublicFulfillmentMetadataValue(value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function typePublicFulfillmentMetadataValue(name, valueIndex) {
|
||||||
|
return cy
|
||||||
|
.get(METADATA_FORM.publicMetaSection)
|
||||||
|
.find(METADATA_FORM.valueField)
|
||||||
|
.eq(valueIndex)
|
||||||
|
.clear()
|
||||||
|
.type(name);
|
||||||
|
}
|
||||||
|
export function typePrivateFulfillmentMetadataValue(name, valueIndex) {
|
||||||
|
return cy
|
||||||
|
.get(METADATA_FORM.privateMetaSection)
|
||||||
|
.find(METADATA_FORM.valueField)
|
||||||
|
.eq(valueIndex)
|
||||||
|
.clear()
|
||||||
|
.type(name);
|
||||||
|
}
|
||||||
|
export function typePublicFulfillmentMetadataName(name, nameIndex) {
|
||||||
|
return cy
|
||||||
|
.get(METADATA_FORM.publicMetaSection)
|
||||||
|
.find(METADATA_FORM.nameInput)
|
||||||
|
.eq(nameIndex)
|
||||||
|
.clear()
|
||||||
|
.type(name);
|
||||||
|
}
|
||||||
|
export function typePrivateFulfillmentMetadataName(name, nameIndex) {
|
||||||
|
return cy
|
||||||
|
.get(METADATA_FORM.privateMetaSection)
|
||||||
|
.find(METADATA_FORM.nameInput)
|
||||||
|
.eq(nameIndex)
|
||||||
|
.clear()
|
||||||
|
.type(name);
|
||||||
|
}
|
||||||
|
export function expandPublicFulfillmentMetadata(fulfillmentIndex) {
|
||||||
|
return cy
|
||||||
|
.get(METADATA_FORM.fulfillmentMetaSection)
|
||||||
|
.eq(fulfillmentIndex)
|
||||||
|
.find(METADATA_FORM.publicMetaSection)
|
||||||
|
.find(METADATA_FORM.metaExpandButton)
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
export function deletePublicFulfillmentMetadata(
|
||||||
|
fulfillmentIndex,
|
||||||
|
metaDataIndex,
|
||||||
|
) {
|
||||||
|
return cy
|
||||||
|
.get(METADATA_FORM.fulfillmentMetaSection)
|
||||||
|
.eq(fulfillmentIndex)
|
||||||
|
.find(METADATA_FORM.publicMetaSection)
|
||||||
|
.find(METADATA_FORM.metaDeletedButton)
|
||||||
|
.eq(metaDataIndex)
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
export function deletePrivateFulfillmentMetadata(
|
||||||
|
fulfillmentIndex,
|
||||||
|
metaDataIndex,
|
||||||
|
) {
|
||||||
|
return cy
|
||||||
|
.get(METADATA_FORM.fulfillmentMetaSection)
|
||||||
|
.eq(fulfillmentIndex)
|
||||||
|
.find(METADATA_FORM.privateMetaSection)
|
||||||
|
.find(METADATA_FORM.metaDeletedButton)
|
||||||
|
.eq(metaDataIndex)
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
export function expandPrivateFulfillmentMetadata(fulfillmentIndex) {
|
||||||
|
return cy
|
||||||
|
.get(METADATA_FORM.fulfillmentMetaSection)
|
||||||
|
.eq(fulfillmentIndex)
|
||||||
|
.find(METADATA_FORM.privateMetaSection)
|
||||||
|
.find(METADATA_FORM.metaExpandButton)
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
export function addPrivateMetadataFieldFulfillmentOrder(
|
||||||
|
fulfillmentIndex,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
) {
|
||||||
|
expandPrivateFulfillmentMetadata(fulfillmentIndex);
|
||||||
|
cy.get(METADATA_FORM.fulfillmentMetaSection)
|
||||||
|
|
||||||
|
.eq(fulfillmentIndex)
|
||||||
|
.find(METADATA_FORM.privateMetaSection)
|
||||||
|
.find(METADATA_FORM.addFieldButton)
|
||||||
|
.click();
|
||||||
|
typePrivateFulfillmentMetadataName(name, 0);
|
||||||
|
typePrivateFulfillmentMetadataValue(value, 0);
|
||||||
|
}
|
||||||
|
export function updatePrivateMetadataFieldFulfillmentOrder(
|
||||||
|
fulfillmentIndex,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
) {
|
||||||
|
expandPrivateFulfillmentMetadata(fulfillmentIndex);
|
||||||
|
typePrivateFulfillmentMetadataName(name, 0);
|
||||||
|
typePrivateFulfillmentMetadataValue(value, 0);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,24 @@
|
||||||
export * as ordersOperationsHelpers from "./ordersOperations";
|
export * as ordersOperationsHelpers from "./ordersOperations";
|
||||||
export * as transactionsOrderUtils from "./ordersTransactionUtils";
|
export * as transactionsOrderUtils from "./ordersTransactionUtils";
|
||||||
|
export {
|
||||||
|
addMetadataField,
|
||||||
|
addPrivateMetadataFieldFulfillmentOrder,
|
||||||
|
addPublicMetadataFieldFulfillmentOrder,
|
||||||
|
deletePrivateFulfillmentMetadata,
|
||||||
|
deletePublicFulfillmentMetadata,
|
||||||
|
expandPrivateFulfillmentMetadata,
|
||||||
|
expandPublicFulfillmentMetadata,
|
||||||
|
updatePrivateMetadataFieldFulfillmentOrder,
|
||||||
|
updatePublicMetadataFieldFulfillmentOrder,
|
||||||
|
} from "./catalog/metadataComponent";
|
||||||
|
export { selectChannelInPicker } from "./channelsPage";
|
||||||
|
export { finalizeDraftOrder } from "./draftOrderPage";
|
||||||
|
export {
|
||||||
|
addNewProductToOrder,
|
||||||
|
applyFixedLineDiscountForProduct,
|
||||||
|
changeQuantityOfProducts,
|
||||||
|
deleteProductFromGridTableOnIndex,
|
||||||
|
} from "./ordersOperations";
|
||||||
export { expectWelcomeMessageIncludes } from "./homePage";
|
export { expectWelcomeMessageIncludes } from "./homePage";
|
||||||
export { getDisplayedSelectors } from "./permissionsPage";
|
export { getDisplayedSelectors } from "./permissionsPage";
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { MetadataInput } from "@dashboard/graphql";
|
||||||
import { ChangeEvent } from "@dashboard/hooks/useForm";
|
import { ChangeEvent } from "@dashboard/hooks/useForm";
|
||||||
import { removeAtIndex, updateAtIndex } from "@dashboard/utils/lists";
|
import { removeAtIndex, updateAtIndex } from "@dashboard/utils/lists";
|
||||||
import { Box } from "@saleor/macaw-ui/next";
|
import { Box } from "@saleor/macaw-ui/next";
|
||||||
import React from "react";
|
import React, { memo } from "react";
|
||||||
|
|
||||||
import { MetadataCard, MetadataCardProps } from "./MetadataCard";
|
import { MetadataCard, MetadataCardProps } from "./MetadataCard";
|
||||||
import { EventDataAction, EventDataField } from "./types";
|
import { EventDataAction, EventDataField } from "./types";
|
||||||
|
@ -11,9 +11,24 @@ import { getDataKey, parseEventData } from "./utils";
|
||||||
export interface MetadataProps
|
export interface MetadataProps
|
||||||
extends Omit<MetadataCardProps, "data" | "isPrivate"> {
|
extends Omit<MetadataCardProps, "data" | "isPrivate"> {
|
||||||
data: Record<"metadata" | "privateMetadata", MetadataInput[]>;
|
data: Record<"metadata" | "privateMetadata", MetadataInput[]>;
|
||||||
|
isLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Metadata: React.FC<MetadataProps> = ({ data, onChange }) => {
|
const propsCompare = (_, newProps: MetadataProps) => {
|
||||||
|
/**
|
||||||
|
If we pass `isLoading` render only when the loading finishes
|
||||||
|
*/
|
||||||
|
if (typeof newProps.isLoading !== "undefined") {
|
||||||
|
return newProps.isLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If `isLoading` is not present, keep the old behavior
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Metadata: React.FC<MetadataProps> = memo(({ data, onChange }) => {
|
||||||
const change = (event: ChangeEvent, isPrivate: boolean) => {
|
const change = (event: ChangeEvent, isPrivate: boolean) => {
|
||||||
const { action, field, fieldIndex, value } = parseEventData(event);
|
const { action, field, fieldIndex, value } = parseEventData(event);
|
||||||
const key = getDataKey(isPrivate);
|
const key = getDataKey(isPrivate);
|
||||||
|
@ -66,4 +81,4 @@ export const Metadata: React.FC<MetadataProps> = ({ data, onChange }) => {
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
}, propsCompare);
|
||||||
|
|
|
@ -19,3 +19,10 @@ export interface MetadataFormData {
|
||||||
metadata: MetadataInput[];
|
metadata: MetadataInput[];
|
||||||
privateMetadata: MetadataInput[];
|
privateMetadata: MetadataInput[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MetadataIdSchema {
|
||||||
|
[key: string]: {
|
||||||
|
metadata: MetadataInput[];
|
||||||
|
privateMetadata: MetadataInput[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -156,6 +156,7 @@ export const fragmentRefundOrderLine = gql`
|
||||||
|
|
||||||
export const fulfillmentFragment = gql`
|
export const fulfillmentFragment = gql`
|
||||||
fragment Fulfillment on Fulfillment {
|
fragment Fulfillment on Fulfillment {
|
||||||
|
...Metadata
|
||||||
id
|
id
|
||||||
lines {
|
lines {
|
||||||
id
|
id
|
||||||
|
|
|
@ -1603,6 +1603,7 @@ export const OrderLineFragmentDoc = gql`
|
||||||
${TaxedMoneyFragmentDoc}`;
|
${TaxedMoneyFragmentDoc}`;
|
||||||
export const FulfillmentFragmentDoc = gql`
|
export const FulfillmentFragmentDoc = gql`
|
||||||
fragment Fulfillment on Fulfillment {
|
fragment Fulfillment on Fulfillment {
|
||||||
|
...Metadata
|
||||||
id
|
id
|
||||||
lines {
|
lines {
|
||||||
id
|
id
|
||||||
|
@ -1619,7 +1620,8 @@ export const FulfillmentFragmentDoc = gql`
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${OrderLineFragmentDoc}`;
|
${MetadataFragmentDoc}
|
||||||
|
${OrderLineFragmentDoc}`;
|
||||||
export const InvoiceFragmentDoc = gql`
|
export const InvoiceFragmentDoc = gql`
|
||||||
fragment Invoice on Invoice {
|
fragment Invoice on Invoice {
|
||||||
id
|
id
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -9,7 +9,7 @@ import { CardSpacer } from "@dashboard/components/CardSpacer";
|
||||||
import { useDevModeContext } from "@dashboard/components/DevModePanel/hooks";
|
import { useDevModeContext } from "@dashboard/components/DevModePanel/hooks";
|
||||||
import Form from "@dashboard/components/Form";
|
import Form from "@dashboard/components/Form";
|
||||||
import { DetailPageLayout } from "@dashboard/components/Layouts";
|
import { DetailPageLayout } from "@dashboard/components/Layouts";
|
||||||
import { Metadata, MetadataFormData } from "@dashboard/components/Metadata";
|
import { Metadata, MetadataIdSchema } from "@dashboard/components/Metadata";
|
||||||
import Savebar from "@dashboard/components/Savebar";
|
import Savebar from "@dashboard/components/Savebar";
|
||||||
import {
|
import {
|
||||||
OrderDetailsFragment,
|
OrderDetailsFragment,
|
||||||
|
@ -22,8 +22,6 @@ import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||||
import { defaultGraphiQLQuery } from "@dashboard/orders/queries";
|
import { defaultGraphiQLQuery } from "@dashboard/orders/queries";
|
||||||
import { orderListUrl } from "@dashboard/orders/urls";
|
import { orderListUrl } from "@dashboard/orders/urls";
|
||||||
import { mapMetadataItemToInput } from "@dashboard/utils/maps";
|
|
||||||
import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger";
|
|
||||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
@ -41,7 +39,12 @@ import { OrderPaymentOrTransaction } from "../OrderPaymentOrTransaction/OrderPay
|
||||||
import OrderUnfulfilledProductsCard from "../OrderUnfulfilledProductsCard";
|
import OrderUnfulfilledProductsCard from "../OrderUnfulfilledProductsCard";
|
||||||
import { messages } from "./messages";
|
import { messages } from "./messages";
|
||||||
import Title from "./Title";
|
import Title from "./Title";
|
||||||
import { filteredConditionalItems, hasAnyItemsReplaceable } from "./utils";
|
import {
|
||||||
|
createMetadataHandler,
|
||||||
|
createOrderMetadataIdSchema,
|
||||||
|
filteredConditionalItems,
|
||||||
|
hasAnyItemsReplaceable,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
export interface OrderDetailsPageProps {
|
export interface OrderDetailsPageProps {
|
||||||
order: OrderDetailsFragment | OrderDetailsFragment;
|
order: OrderDetailsFragment | OrderDetailsFragment;
|
||||||
|
@ -80,7 +83,7 @@ export interface OrderDetailsPageProps {
|
||||||
onInvoiceSend(invoiceId: string);
|
onInvoiceSend(invoiceId: string);
|
||||||
onTransactionAction(transactionId: string, actionType: TransactionActionEnum);
|
onTransactionAction(transactionId: string, actionType: TransactionActionEnum);
|
||||||
onAddManualTransaction();
|
onAddManualTransaction();
|
||||||
onSubmit(data: MetadataFormData): SubmitPromise;
|
onSubmit(data: MetadataIdSchema): SubmitPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
|
@ -118,13 +121,6 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const {
|
|
||||||
isMetadataModified,
|
|
||||||
isPrivateMetadataModified,
|
|
||||||
makeChangeHandler: makeMetadataChangeHandler,
|
|
||||||
resetMetadataChanged,
|
|
||||||
} = useMetadataChangeTrigger();
|
|
||||||
|
|
||||||
const isOrderUnconfirmed = order?.status === OrderStatus.UNCONFIRMED;
|
const isOrderUnconfirmed = order?.status === OrderStatus.UNCONFIRMED;
|
||||||
const canCancel = order?.status !== OrderStatus.CANCELED;
|
const canCancel = order?.status !== OrderStatus.CANCELED;
|
||||||
const canEditAddresses = order?.status !== OrderStatus.CANCELED;
|
const canEditAddresses = order?.status !== OrderStatus.CANCELED;
|
||||||
|
@ -137,24 +133,12 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
line => line.quantityToFulfill > 0,
|
line => line.quantityToFulfill > 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = async (data: MetadataFormData) => {
|
const handleSubmit = async (data: MetadataIdSchema) => {
|
||||||
const metadata = isMetadataModified ? data.metadata : undefined;
|
const result = await onSubmit(data);
|
||||||
const privateMetadata = isPrivateMetadataModified
|
|
||||||
? data.privateMetadata
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const result = await onSubmit({
|
|
||||||
metadata,
|
|
||||||
privateMetadata,
|
|
||||||
});
|
|
||||||
resetMetadataChanged();
|
|
||||||
return getMutationErrors(result);
|
return getMutationErrors(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
const initial: MetadataFormData = {
|
const initial = createOrderMetadataIdSchema(order);
|
||||||
metadata: order?.metadata.map(mapMetadataItemToInput),
|
|
||||||
privateMetadata: order?.privateMetadata.map(mapMetadataItemToInput),
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveLabel = isOrderUnconfirmed
|
const saveLabel = isOrderUnconfirmed
|
||||||
? { confirm: intl.formatMessage(messages.confirmOrder) }
|
? { confirm: intl.formatMessage(messages.confirmOrder) }
|
||||||
|
@ -204,9 +188,18 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form confirmLeave initial={initial} onSubmit={handleSubmit}>
|
<Form
|
||||||
{({ change, data, submit }) => {
|
confirmLeave
|
||||||
const changeMetadata = makeMetadataChangeHandler(change);
|
initial={initial}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
mergeData={false}
|
||||||
|
>
|
||||||
|
{({ set, triggerChange, data, submit }) => {
|
||||||
|
const handleChangeMetadata = createMetadataHandler(
|
||||||
|
data,
|
||||||
|
set,
|
||||||
|
triggerChange,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DetailPageLayout>
|
<DetailPageLayout>
|
||||||
|
@ -248,22 +241,28 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{order?.fulfillments?.map(fulfillment => (
|
{order?.fulfillments?.map(fulfillment => (
|
||||||
<React.Fragment key={fulfillment.id}>
|
<OrderFulfilledProductsCard
|
||||||
<OrderFulfilledProductsCard
|
dataTestId="fulfilled-order-section"
|
||||||
fulfillment={fulfillment}
|
key={fulfillment.id}
|
||||||
fulfillmentAllowUnpaid={shop?.fulfillmentAllowUnpaid}
|
fulfillment={fulfillment}
|
||||||
order={order}
|
fulfillmentAllowUnpaid={shop?.fulfillmentAllowUnpaid}
|
||||||
onOrderFulfillmentCancel={() =>
|
order={order}
|
||||||
onFulfillmentCancel(fulfillment.id)
|
onOrderFulfillmentCancel={() =>
|
||||||
}
|
onFulfillmentCancel(fulfillment.id)
|
||||||
onTrackingCodeAdd={() =>
|
}
|
||||||
onFulfillmentTrackingNumberUpdate(fulfillment.id)
|
onTrackingCodeAdd={() =>
|
||||||
}
|
onFulfillmentTrackingNumberUpdate(fulfillment.id)
|
||||||
onOrderFulfillmentApprove={() =>
|
}
|
||||||
onFulfillmentApprove(fulfillment.id)
|
onOrderFulfillmentApprove={() =>
|
||||||
}
|
onFulfillmentApprove(fulfillment.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Metadata
|
||||||
|
isLoading={loading}
|
||||||
|
data={data[fulfillment.id]}
|
||||||
|
onChange={x => handleChangeMetadata(x, fulfillment.id)}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</OrderFulfilledProductsCard>
|
||||||
))}
|
))}
|
||||||
<OrderPaymentOrTransaction
|
<OrderPaymentOrTransaction
|
||||||
order={order}
|
order={order}
|
||||||
|
@ -275,7 +274,11 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
onMarkAsPaid={onMarkAsPaid}
|
onMarkAsPaid={onMarkAsPaid}
|
||||||
onAddManualTransaction={onAddManualTransaction}
|
onAddManualTransaction={onAddManualTransaction}
|
||||||
/>
|
/>
|
||||||
<Metadata data={data} onChange={changeMetadata} />
|
<Metadata
|
||||||
|
isLoading={loading}
|
||||||
|
data={data[order?.id]}
|
||||||
|
onChange={x => handleChangeMetadata(x, order?.id)}
|
||||||
|
/>
|
||||||
<OrderHistory
|
<OrderHistory
|
||||||
history={order?.events}
|
history={order?.events}
|
||||||
orderCurrency={order?.total?.gross.currency}
|
orderCurrency={order?.total?.gross.currency}
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import { filteredConditionalItems } from "./utils";
|
import { OrderDetailsFragment } from "@dashboard/graphql";
|
||||||
|
import { ChangeEvent } from "@dashboard/hooks/useForm";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createMetadataHandler,
|
||||||
|
createOrderMetadataIdSchema,
|
||||||
|
filteredConditionalItems,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
describe("filteredConditionalItems", () => {
|
describe("filteredConditionalItems", () => {
|
||||||
it("should return empty [] when no items has shouldExist set to true", () => {
|
it("should return empty [] when no items has shouldExist set to true", () => {
|
||||||
|
@ -42,3 +49,110 @@ describe("filteredConditionalItems", () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("createOrderMetadataIdSchema", () => {
|
||||||
|
it("returns order and fulfilment metadata", () => {
|
||||||
|
// Arrange
|
||||||
|
const order = {
|
||||||
|
id: "some-order-id",
|
||||||
|
metadata: [{ key: "mt1", value: "mt1-value" }],
|
||||||
|
privateMetadata: [{ key: "pmt1", value: "pmt1-value" }],
|
||||||
|
fulfillments: [
|
||||||
|
{
|
||||||
|
id: "some-fulfillment-id",
|
||||||
|
metadata: [{ key: "fmt1", value: "fmt1-value" }],
|
||||||
|
privateMetadata: [{ key: "fpmt1", value: "fpmt1-value" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "some-fulfillment-id",
|
||||||
|
metadata: [{ key: "fmt1", value: "fmt1-value" }],
|
||||||
|
privateMetadata: [{ key: "fpmt1", value: "fpmt1-value" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const metadata = createOrderMetadataIdSchema(order as OrderDetailsFragment);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(metadata).toEqual({
|
||||||
|
"some-fulfillment-id": {
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: "fmt1",
|
||||||
|
value: "fmt1-value",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
privateMetadata: [
|
||||||
|
{
|
||||||
|
key: "fpmt1",
|
||||||
|
value: "fpmt1-value",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"some-order-id": {
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: "mt1",
|
||||||
|
value: "mt1-value",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
privateMetadata: [
|
||||||
|
{
|
||||||
|
key: "pmt1",
|
||||||
|
value: "pmt1-value",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createMetadataHandler", () => {
|
||||||
|
it("handles order metadata change", () => {
|
||||||
|
// Arrange
|
||||||
|
const currentData = {
|
||||||
|
"some-order-id": {
|
||||||
|
metadata: [{ key: "mt1", value: "mt1-value" }],
|
||||||
|
privateMetadata: [{ key: "pmt1", value: "pmt1-value" }],
|
||||||
|
},
|
||||||
|
"some-fulfillment-id": {
|
||||||
|
metadata: [{ key: "fmt1", value: "fmt1-value" }],
|
||||||
|
privateMetadata: [{ key: "fpmt1", value: "fpmt1-value" }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const set = jest.fn();
|
||||||
|
const triggerChange = jest.fn();
|
||||||
|
const handler = createMetadataHandler(currentData, set, triggerChange);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
handler(
|
||||||
|
{
|
||||||
|
target: {
|
||||||
|
name: "metadata",
|
||||||
|
value: [{ key: "new-key", value: "new-value" }],
|
||||||
|
},
|
||||||
|
} as ChangeEvent,
|
||||||
|
"some-order-id",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(set).toHaveBeenCalledWith({
|
||||||
|
"some-order-id": {
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: "new-key",
|
||||||
|
value: "new-value",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
privateMetadata: [
|
||||||
|
{
|
||||||
|
key: "pmt1",
|
||||||
|
value: "pmt1-value",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(triggerChange).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
import { MetadataIdSchema } from "@dashboard/components/Metadata";
|
||||||
import { OrderDetailsFragment } from "@dashboard/graphql";
|
import { OrderDetailsFragment } from "@dashboard/graphql";
|
||||||
|
import { ChangeEvent } from "@dashboard/hooks/useForm";
|
||||||
|
import { mapMetadataItemToInput } from "@dashboard/utils/maps";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getFulfilledFulfillemnts,
|
getFulfilledFulfillemnts,
|
||||||
|
@ -29,3 +32,37 @@ export interface ConditionalItem {
|
||||||
|
|
||||||
export const filteredConditionalItems = (items: ConditionalItem[]) =>
|
export const filteredConditionalItems = (items: ConditionalItem[]) =>
|
||||||
items.filter(({ shouldExist }) => shouldExist).map(({ item }) => item);
|
items.filter(({ shouldExist }) => shouldExist).map(({ item }) => item);
|
||||||
|
|
||||||
|
export const createOrderMetadataIdSchema = (
|
||||||
|
order: OrderDetailsFragment,
|
||||||
|
): MetadataIdSchema => ({
|
||||||
|
[order?.id]: {
|
||||||
|
metadata: order?.metadata.map(mapMetadataItemToInput),
|
||||||
|
privateMetadata: order?.privateMetadata.map(mapMetadataItemToInput),
|
||||||
|
},
|
||||||
|
...order?.fulfillments.reduce((p, c) => {
|
||||||
|
p[c.id] = {
|
||||||
|
metadata: c?.metadata.map(mapMetadataItemToInput),
|
||||||
|
privateMetadata: c?.privateMetadata.map(mapMetadataItemToInput),
|
||||||
|
};
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}, {}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createMetadataHandler =
|
||||||
|
(
|
||||||
|
currentData: MetadataIdSchema,
|
||||||
|
set: (newData: Partial<MetadataIdSchema>) => void,
|
||||||
|
triggerChange: () => void,
|
||||||
|
) =>
|
||||||
|
(event: ChangeEvent, objectId: string) => {
|
||||||
|
const metadataType = event.target.name;
|
||||||
|
set({
|
||||||
|
[objectId]: {
|
||||||
|
...currentData[objectId],
|
||||||
|
[metadataType]: [...event.target.value],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
triggerChange();
|
||||||
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
|
||||||
import { FulfillmentStatus, OrderDetailsFragment } from "@dashboard/graphql";
|
import { FulfillmentStatus, OrderDetailsFragment } from "@dashboard/graphql";
|
||||||
import TrashIcon from "@dashboard/icons/Trash";
|
import TrashIcon from "@dashboard/icons/Trash";
|
||||||
import { orderHasTransactions } from "@dashboard/orders/types";
|
import { orderHasTransactions } from "@dashboard/orders/types";
|
||||||
import { mergeRepeatedOrderLines } from "@dashboard/orders/utils/data";
|
import { mergeRepeatedOrderLines } from "@dashboard/orders/utils/data";
|
||||||
import { Card, CardContent } from "@material-ui/core";
|
import { CardContent } from "@material-ui/core";
|
||||||
import { IconButton } from "@saleor/macaw-ui";
|
import { IconButton } from "@saleor/macaw-ui";
|
||||||
|
import { Box, Divider } from "@saleor/macaw-ui/next";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import OrderCardTitle from "../OrderCardTitle";
|
import OrderCardTitle from "../OrderCardTitle";
|
||||||
|
@ -20,6 +20,7 @@ interface OrderFulfilledProductsCardProps {
|
||||||
onOrderFulfillmentApprove: () => void;
|
onOrderFulfillmentApprove: () => void;
|
||||||
onOrderFulfillmentCancel: () => void;
|
onOrderFulfillmentCancel: () => void;
|
||||||
onTrackingCodeAdd: () => void;
|
onTrackingCodeAdd: () => void;
|
||||||
|
dataTestId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusesToMergeLines = [
|
const statusesToMergeLines = [
|
||||||
|
@ -43,6 +44,7 @@ const OrderFulfilledProductsCard: React.FC<
|
||||||
onOrderFulfillmentApprove,
|
onOrderFulfillmentApprove,
|
||||||
onOrderFulfillmentCancel,
|
onOrderFulfillmentCancel,
|
||||||
onTrackingCodeAdd,
|
onTrackingCodeAdd,
|
||||||
|
dataTestId,
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
|
||||||
|
@ -61,17 +63,17 @@ const OrderFulfilledProductsCard: React.FC<
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box data-test-id={dataTestId}>
|
||||||
<Card>
|
<OrderCardTitle
|
||||||
<OrderCardTitle
|
withStatus
|
||||||
withStatus
|
lines={fulfillment?.lines}
|
||||||
lines={fulfillment?.lines}
|
fulfillmentOrder={fulfillment?.fulfillmentOrder}
|
||||||
fulfillmentOrder={fulfillment?.fulfillmentOrder}
|
status={fulfillment?.status}
|
||||||
status={fulfillment?.status}
|
warehouseName={fulfillment?.warehouse?.name}
|
||||||
warehouseName={fulfillment?.warehouse?.name}
|
orderNumber={order?.number}
|
||||||
orderNumber={order?.number}
|
toolbar={
|
||||||
toolbar={
|
<Box display="flex" alignItems="center" gap={6}>
|
||||||
cancelableStatuses.includes(fulfillment?.status) && (
|
{cancelableStatuses.includes(fulfillment?.status) && (
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className={classes.deleteIcon}
|
className={classes.deleteIcon}
|
||||||
|
@ -80,26 +82,27 @@ const OrderFulfilledProductsCard: React.FC<
|
||||||
>
|
>
|
||||||
<TrashIcon />
|
<TrashIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)
|
)}
|
||||||
}
|
<ActionButtons
|
||||||
/>
|
orderId={order?.id}
|
||||||
<CardContent>
|
status={fulfillment?.status}
|
||||||
<OrderDetailsDatagrid lines={getLines()} loading={false} />
|
trackingNumber={fulfillment?.trackingNumber}
|
||||||
<ExtraInfoLines fulfillment={fulfillment} />
|
orderIsPaid={order?.isPaid}
|
||||||
<ActionButtons
|
fulfillmentAllowUnpaid={fulfillmentAllowUnpaid}
|
||||||
orderId={order?.id}
|
onTrackingCodeAdd={onTrackingCodeAdd}
|
||||||
status={fulfillment?.status}
|
onApprove={onOrderFulfillmentApprove}
|
||||||
trackingNumber={fulfillment?.trackingNumber}
|
hasTransactions={orderHasTransactions(order)}
|
||||||
orderIsPaid={order?.isPaid}
|
/>
|
||||||
fulfillmentAllowUnpaid={fulfillmentAllowUnpaid}
|
</Box>
|
||||||
onTrackingCodeAdd={onTrackingCodeAdd}
|
}
|
||||||
onApprove={onOrderFulfillmentApprove}
|
/>
|
||||||
hasTransactions={orderHasTransactions(order)}
|
<CardContent>
|
||||||
/>
|
<OrderDetailsDatagrid lines={getLines()} loading={false} />
|
||||||
</CardContent>
|
<ExtraInfoLines fulfillment={fulfillment} />
|
||||||
</Card>
|
</CardContent>
|
||||||
<CardSpacer />
|
{props.children}
|
||||||
</>
|
<Divider />
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1207,6 +1207,8 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
__typename: "Fulfillment",
|
__typename: "Fulfillment",
|
||||||
fulfillmentOrder: 2,
|
fulfillmentOrder: 2,
|
||||||
id: "RnVsZmlsbG1lbnQ6MjQ=",
|
id: "RnVsZmlsbG1lbnQ6MjQ=",
|
||||||
|
metadata: [],
|
||||||
|
privateMetadata: [],
|
||||||
lines: [
|
lines: [
|
||||||
{
|
{
|
||||||
__typename: "FulfillmentLine",
|
__typename: "FulfillmentLine",
|
||||||
|
@ -1331,6 +1333,8 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
__typename: "Fulfillment",
|
__typename: "Fulfillment",
|
||||||
fulfillmentOrder: 1,
|
fulfillmentOrder: 1,
|
||||||
id: "RnVsZmlsbG1lbnQ6OQ==",
|
id: "RnVsZmlsbG1lbnQ6OQ==",
|
||||||
|
metadata: [],
|
||||||
|
privateMetadata: [],
|
||||||
lines: [
|
lines: [
|
||||||
{
|
{
|
||||||
__typename: "FulfillmentLine",
|
__typename: "FulfillmentLine",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useApolloClient } from "@apollo/client";
|
import { useApolloClient } from "@apollo/client";
|
||||||
import { MetadataFormData } from "@dashboard/components/Metadata";
|
import { MetadataIdSchema } from "@dashboard/components/Metadata";
|
||||||
import NotFoundPage from "@dashboard/components/NotFoundPage";
|
import NotFoundPage from "@dashboard/components/NotFoundPage";
|
||||||
import { Task } from "@dashboard/containers/BackgroundTasks/types";
|
import { Task } from "@dashboard/containers/BackgroundTasks/types";
|
||||||
import {
|
import {
|
||||||
|
@ -15,6 +15,7 @@ import useBackgroundTask from "@dashboard/hooks/useBackgroundTask";
|
||||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||||
import { commonMessages } from "@dashboard/intl";
|
import { commonMessages } from "@dashboard/intl";
|
||||||
|
import { createOrderMetadataIdSchema } from "@dashboard/orders/components/OrderDetailsPage/utils";
|
||||||
import getOrderErrorMessage from "@dashboard/utils/errors/order";
|
import getOrderErrorMessage from "@dashboard/utils/errors/order";
|
||||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||||
import createMetadataUpdateHandler from "@dashboard/utils/handlers/metadataUpdateHandler";
|
import createMetadataUpdateHandler from "@dashboard/utils/handlers/metadataUpdateHandler";
|
||||||
|
@ -83,21 +84,27 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
const isOrderUnconfirmed = order?.status === OrderStatus.UNCONFIRMED;
|
const isOrderUnconfirmed = order?.status === OrderStatus.UNCONFIRMED;
|
||||||
const isOrderDraft = order?.status === OrderStatus.DRAFT;
|
const isOrderDraft = order?.status === OrderStatus.DRAFT;
|
||||||
|
|
||||||
const handleSubmit = async (data: MetadataFormData) => {
|
const handleSubmit = async (data: MetadataIdSchema) => {
|
||||||
if (order?.status === OrderStatus.UNCONFIRMED) {
|
if (order?.status === OrderStatus.UNCONFIRMED) {
|
||||||
await orderConfirm({ variables: { id: order?.id } });
|
await orderConfirm({ variables: { id: order?.id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
const update = createMetadataUpdateHandler(
|
const initial = createOrderMetadataIdSchema(order);
|
||||||
order,
|
const metadataPromises = Object.entries(initial).map(([id, metaEntry]) => {
|
||||||
() => Promise.resolve([]),
|
const update = createMetadataUpdateHandler(
|
||||||
variables => updateMetadata({ variables }),
|
{ ...metaEntry, id },
|
||||||
variables => updatePrivateMetadata({ variables }),
|
() => Promise.resolve([]),
|
||||||
);
|
variables => updateMetadata({ variables }),
|
||||||
|
variables => updatePrivateMetadata({ variables }),
|
||||||
|
);
|
||||||
|
|
||||||
const result = await update(data);
|
return update(data[id]);
|
||||||
|
});
|
||||||
|
|
||||||
if (result.length === 0) {
|
const result = await Promise.all(metadataPromises);
|
||||||
|
const errors = result.reduce((p, c) => p.concat(c), []);
|
||||||
|
|
||||||
|
if (errors.length === 0) {
|
||||||
notify({
|
notify({
|
||||||
status: "success",
|
status: "success",
|
||||||
text: intl.formatMessage(commonMessages.savedChanges),
|
text: intl.formatMessage(commonMessages.savedChanges),
|
||||||
|
@ -182,6 +189,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
|
||||||
<OrderNormalDetails
|
<OrderNormalDetails
|
||||||
id={id}
|
id={id}
|
||||||
params={params}
|
params={params}
|
||||||
|
loading={loading}
|
||||||
data={data}
|
data={data}
|
||||||
orderAddNote={orderAddNote}
|
orderAddNote={orderAddNote}
|
||||||
orderInvoiceRequest={orderInvoiceRequest}
|
orderInvoiceRequest={orderInvoiceRequest}
|
||||||
|
|
|
@ -64,6 +64,7 @@ interface OrderNormalDetailsProps {
|
||||||
id: string;
|
id: string;
|
||||||
params: OrderUrlQueryParams;
|
params: OrderUrlQueryParams;
|
||||||
data: OrderDetailsQueryResult["data"];
|
data: OrderDetailsQueryResult["data"];
|
||||||
|
loading: boolean;
|
||||||
orderAddNote: any;
|
orderAddNote: any;
|
||||||
orderInvoiceRequest: any;
|
orderInvoiceRequest: any;
|
||||||
handleSubmit: any;
|
handleSubmit: any;
|
||||||
|
@ -104,6 +105,7 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
id,
|
id,
|
||||||
params,
|
params,
|
||||||
data,
|
data,
|
||||||
|
loading,
|
||||||
orderAddNote,
|
orderAddNote,
|
||||||
orderInvoiceRequest,
|
orderInvoiceRequest,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
@ -187,7 +189,9 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
<OrderDetailsPage
|
<OrderDetailsPage
|
||||||
onOrderReturn={() => navigate(orderReturnUrl(id))}
|
onOrderReturn={() => navigate(orderReturnUrl(id))}
|
||||||
loading={
|
loading={
|
||||||
updateMetadataOpts.loading || updatePrivateMetadataOpts.loading
|
loading ||
|
||||||
|
updateMetadataOpts.loading ||
|
||||||
|
updatePrivateMetadataOpts.loading
|
||||||
}
|
}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
onNoteAdd={variables =>
|
onNoteAdd={variables =>
|
||||||
|
|
|
@ -52,11 +52,10 @@ function createMetadataUpdateHandler<TData extends MetadataFormData, TError>(
|
||||||
if (data.metadata && hasMetadataChanged) {
|
if (data.metadata && hasMetadataChanged) {
|
||||||
const initialKeys = initial.metadata.map(m => m.key);
|
const initialKeys = initial.metadata.map(m => m.key);
|
||||||
const modifiedKeys = data.metadata.map(m => m.key);
|
const modifiedKeys = data.metadata.map(m => m.key);
|
||||||
|
|
||||||
const keyDiff = arrayDiff(initialKeys, modifiedKeys);
|
const keyDiff = arrayDiff(initialKeys, modifiedKeys);
|
||||||
const metadataInput = filterMetadataArray(data.metadata);
|
const metadataInput = filterMetadataArray(data.metadata);
|
||||||
|
|
||||||
if (metadataInput.length) {
|
if (metadataInput.length || keyDiff.removed.length) {
|
||||||
const updateMetaResult = await updateMetadata({
|
const updateMetaResult = await updateMetadata({
|
||||||
id: initial.id,
|
id: initial.id,
|
||||||
input: metadataInput,
|
input: metadataInput,
|
||||||
|
@ -81,7 +80,7 @@ function createMetadataUpdateHandler<TData extends MetadataFormData, TError>(
|
||||||
const keyDiff = arrayDiff(initialKeys, modifiedKeys);
|
const keyDiff = arrayDiff(initialKeys, modifiedKeys);
|
||||||
const privateMetadataInput = filterMetadataArray(data.privateMetadata);
|
const privateMetadataInput = filterMetadataArray(data.privateMetadata);
|
||||||
|
|
||||||
if (privateMetadataInput.length) {
|
if (privateMetadataInput.length || keyDiff.removed.length) {
|
||||||
const updatePrivateMetaResult = await updatePrivateMetadata({
|
const updatePrivateMetaResult = await updatePrivateMetadata({
|
||||||
id: initial.id,
|
id: initial.id,
|
||||||
input: privateMetadataInput,
|
input: privateMetadataInput,
|
||||||
|
|
Loading…
Reference in a new issue