mirror of
https://github.com/openshiporg/openship.git
synced 2026-06-19 07:35:55 +00:00
293 lines
8.1 KiB
TypeScript
293 lines
8.1 KiB
TypeScript
import { list } from "@keystone-6/core";
|
|
import { allOperations } from "@keystone-6/core/access";
|
|
import {
|
|
checkbox,
|
|
float,
|
|
integer,
|
|
json,
|
|
relationship,
|
|
text,
|
|
timestamp,
|
|
} from "@keystone-6/core/fields";
|
|
|
|
import { isSignedIn, permissions, rules } from "../access";
|
|
import { trackingFields } from "./trackingFields";
|
|
import { placeMultipleOrders } from "../lib/placeMultipleOrders";
|
|
import { getMatches } from "../extendGraphqlSchema/mutations/addMatchToCart";
|
|
|
|
async function applyDynamicWhereClause(context: any, linkId: string, orderId: string) {
|
|
const link = await context.query.Link.findOne({
|
|
where: { id: linkId },
|
|
query: 'id dynamicWhereClause',
|
|
});
|
|
|
|
if (!link) {
|
|
return null;
|
|
}
|
|
|
|
// dynamicWhereClause is now a virtual field that returns a proper where clause object
|
|
// If empty object or not set, it means match all orders
|
|
const dynamicWhere = link.dynamicWhereClause;
|
|
|
|
if (!dynamicWhere || typeof dynamicWhere !== 'object' || Object.keys(dynamicWhere).length === 0) {
|
|
// No filters means the link matches all orders - return the order
|
|
return await context.query.Order.findOne({
|
|
where: { id: orderId },
|
|
query: 'id',
|
|
});
|
|
}
|
|
|
|
// Use findMany with the dynamic filters + id filter
|
|
const matchedOrders = await context.query.Order.findMany({
|
|
where: {
|
|
...dynamicWhere,
|
|
id: { equals: orderId },
|
|
},
|
|
query: 'id',
|
|
take: 1,
|
|
});
|
|
|
|
return matchedOrders.length > 0 ? matchedOrders[0] : null;
|
|
}
|
|
|
|
export const Order = list({
|
|
access: {
|
|
operation: {
|
|
create: isSignedIn,
|
|
query: isSignedIn,
|
|
update: isSignedIn,
|
|
delete: permissions.canManageOrders,
|
|
},
|
|
filter: {
|
|
query: rules.canReadOrders,
|
|
update: rules.canManageOrders,
|
|
delete: rules.canManageOrders,
|
|
},
|
|
},
|
|
hooks: {
|
|
resolveInput: {
|
|
create: ({ operation, resolvedData, context }) => {
|
|
// Auto-assign user if not provided
|
|
if (!resolvedData.user && context.session?.itemId) {
|
|
return {
|
|
...resolvedData,
|
|
user: { connect: { id: context.session.itemId } },
|
|
};
|
|
}
|
|
return resolvedData;
|
|
},
|
|
},
|
|
afterOperation: async ({ operation, item, context }: any) => {
|
|
if (operation === "create") {
|
|
const sudoContext = context.sudo();
|
|
|
|
const order = await sudoContext.query.Order.findOne({
|
|
where: { id: item.id },
|
|
query: `
|
|
id
|
|
user {
|
|
id
|
|
}
|
|
shop {
|
|
id
|
|
name
|
|
linkMode
|
|
links {
|
|
id
|
|
rank
|
|
channel {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
}
|
|
lineItems {
|
|
name
|
|
price
|
|
lineItemId
|
|
quantity
|
|
image
|
|
productId
|
|
variantId
|
|
sku
|
|
}
|
|
cartItemsCount
|
|
`,
|
|
});
|
|
|
|
if (item.linkOrder && order.shop?.links.length > 0) {
|
|
const links = order.shop.links.sort((a: any, b: any) => a.rank - b.rank);
|
|
let matchedLinks: any[] = [];
|
|
|
|
if (order.shop.linkMode === 'sequential') {
|
|
for (const link of links) {
|
|
const matchedOrder = await applyDynamicWhereClause(sudoContext, link.id, order.id);
|
|
if (matchedOrder) {
|
|
matchedLinks.push(link);
|
|
break;
|
|
}
|
|
}
|
|
} else if (order.shop.linkMode === 'simultaneous') {
|
|
for (const link of links) {
|
|
const matchedOrder = await applyDynamicWhereClause(sudoContext, link.id, order.id);
|
|
if (matchedOrder) {
|
|
matchedLinks.push(link);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (matchedLinks.length > 0) {
|
|
for (const link of matchedLinks) {
|
|
await sudoContext.query.CartItem.createMany({
|
|
data: order.lineItems.map((c: any) => ({
|
|
...c,
|
|
channel: { connect: { id: link.channel.id } },
|
|
order: { connect: { id: item.id } },
|
|
user: { connect: { id: order.user?.id } },
|
|
})),
|
|
});
|
|
}
|
|
|
|
if (item.processOrder) {
|
|
await placeMultipleOrders({
|
|
ids: [item.id],
|
|
query: sudoContext.query,
|
|
});
|
|
}
|
|
} else {
|
|
await sudoContext.query.Order.updateOne({
|
|
where: { id: item.id },
|
|
data: {
|
|
error: "No matching link found for this order",
|
|
},
|
|
});
|
|
}
|
|
} else if (item.matchOrder) {
|
|
if (item.matchOrder) {
|
|
try {
|
|
const cartItemsFromMatch = await getMatches({
|
|
orderId: item.id,
|
|
context: sudoContext,
|
|
});
|
|
|
|
const order = await sudoContext.query.Order.findOne({
|
|
where: { id: item.id },
|
|
query: `id error`,
|
|
});
|
|
if (order?.error) {
|
|
const updatedOrder = await sudoContext.query.Order.updateOne({
|
|
where: { id: item.id },
|
|
data: {
|
|
status: "PENDING",
|
|
},
|
|
});
|
|
} else {
|
|
if (item.processOrder) {
|
|
const processedOrder = await placeMultipleOrders({
|
|
ids: [item.id],
|
|
query: sudoContext.query,
|
|
});
|
|
}
|
|
}
|
|
} catch (matchError) {
|
|
// Handle match errors gracefully - don't break order creation
|
|
console.error('Error during match processing:', matchError);
|
|
await sudoContext.query.Order.updateOne({
|
|
where: { id: item.id },
|
|
data: {
|
|
error: `Match processing failed: ${matchError instanceof Error ? matchError.message : 'Unknown error'}`,
|
|
status: "PENDING",
|
|
},
|
|
});
|
|
}
|
|
}
|
|
} else if (order.cartItemsCount > 0 && item.processOrder) {
|
|
// Process the order if there are cart items and processOrder is true
|
|
const processedOrder = await placeMultipleOrders({
|
|
ids: [item.id],
|
|
query: sudoContext.query,
|
|
});
|
|
}
|
|
}
|
|
},
|
|
},
|
|
ui: {
|
|
listView: {
|
|
initialColumns: ["orderId", "orderName", "email", "totalPrice", "shop"],
|
|
},
|
|
},
|
|
fields: {
|
|
// Order identifiers
|
|
orderId: text({
|
|
isIndexed: "unique",
|
|
validation: { isRequired: true },
|
|
}),
|
|
orderName: text(),
|
|
email: text(),
|
|
|
|
// Customer information
|
|
firstName: text(),
|
|
lastName: text(),
|
|
streetAddress1: text(),
|
|
streetAddress2: text(),
|
|
city: text(),
|
|
state: text(),
|
|
zip: text(),
|
|
country: text(),
|
|
phone: text(),
|
|
|
|
// Pricing
|
|
currency: text(),
|
|
totalPrice: float(),
|
|
subTotalPrice: float(),
|
|
totalDiscounts: float(),
|
|
totalTax: float(),
|
|
|
|
// Processing flags
|
|
linkOrder: checkbox({ defaultValue: true }),
|
|
matchOrder: checkbox({ defaultValue: true }),
|
|
processOrder: checkbox({ defaultValue: true }),
|
|
|
|
// Status tracking
|
|
status: text({ defaultValue: "PENDING" }),
|
|
error: text({
|
|
ui: {
|
|
displayMode: "textarea",
|
|
},
|
|
}),
|
|
|
|
// Metadata
|
|
orderMetadata: json(),
|
|
|
|
// Relationships
|
|
shop: relationship({
|
|
ref: "Shop.orders",
|
|
ui: {
|
|
displayMode: "cards",
|
|
cardFields: ["name", "domain"],
|
|
inlineCreate: { fields: ["name", "domain"] },
|
|
inlineEdit: { fields: ["name", "domain"] },
|
|
},
|
|
}),
|
|
lineItems: relationship({
|
|
ref: "LineItem.order",
|
|
many: true,
|
|
ui: {
|
|
displayMode: "cards",
|
|
cardFields: ["name", "quantity", "price"],
|
|
inlineCreate: { fields: ["name", "quantity", "price"] },
|
|
inlineEdit: { fields: ["name", "quantity", "price"] },
|
|
},
|
|
}),
|
|
cartItems: relationship({
|
|
ref: "CartItem.order",
|
|
many: true,
|
|
}),
|
|
user: relationship({
|
|
ref: "User.orders",
|
|
}),
|
|
|
|
...trackingFields,
|
|
},
|
|
});
|