Discuss and resolve questions on Liquid, JavaScript, themes, sales channels, and site speed enhancements.
We're moving the community! Starting July 7, the current community will be read-only for approx. 2 weeks. You can browse content, but posting will be temporarily unavailable. Learn more
I have a checkout extension, and I need to fetch all orders from customers that are associated with a particular company. I need to do this so I can find out if a company has unpaid orders. This is used in a credit app, to determine how much credit a customer has available. But I get the error "Field 'customers' doesn't exist on type 'QueryRoot'
import {useEffect, useState} from 'react';
import {
Banner,
reactExtension,
useBuyerJourneyIntercept,
usePurchasingCompany,
useApi,
useTranslate,
useSettings,
useTotalAmount
} from "@shopify/ui-extensions-react/checkout";
// 1. Choose an extension target
export default reactExtension("purchase.checkout.block.render", () => (
<Extension />
));
function Extension() {
const translate = useTranslate();
//const customer = useCustomer();
const {extension, query} = useApi();
const [balance, setBalance] = useState(-1);
const [done, setDone] = useState(false);
const {credit_allowed} = useSettings();
const {amount, currencyCode} = useTotalAmount();
const [error, setError] = useState('');
const companyData = usePurchasingCompany();
const [otherErrors, setOtherErrors]= useState([]);
const [customers, setCustomers] = useState([]);
const fetchCustomers = () => {
query(`
{
customers(first: 250, query:"companyContactProfiles.company.id:`+companyData.company.id +`"){
edges {
node {
id
companyContactProfiles{
edges{
node {
company{
id
}
}
}
}
}
}
}
}`).then(({data, errors}) => {
setOtherErrors(errors)
const len = data.edges.length;
let out = [];
for(let i= 0; i < len; i++){
out.push(data.edges[i]['node']['id']);
}
setCustomers(out);
}).catch((ex)=>{
setError(ex);
setDone(true);
});
}
const fetchOrders = (customer_id, cursor) => {
return new Promise((resolve, reject) => {
if (cursor === null) {
resolve([])
return;
}
query(`
query{
customer(id: `+customer_id+`)
orders(first: 250 ` + (cursor !== '' ? ', after: ' + cursor : '') + `, query: "company.id:`+companyData.company.id +`") {
edges {
node {
company{
id
}
totalPriceSet {
shopMoney {
amount
}
}
cancelledAt
displayFinancialStatus
refunds {
totalRefunded {
amount
}
}
}
cursor
}
pageInfo {
hasNextPage
}
}
}
}`,
).then(({data, errors}) => {
setOtherErrors(errors)
let curs = null;
if (data.customer.orders.pageInfo.hasNextPage) {
curs = data.customer.orders.edges[data.customer.orders.edges.length - 1].cursor;
}
fetchOrders(customer_id, curs).then((ord) => {
let out = [];
if (data.customer.orders.edges.length > 0) {
out = data.customer.orders.edges;
}
resolve(out.concat(ord));
})
}).catch((err) => {
reject(err);
});
});
}
const fetchCustomerOrders = () =>{
return new Promise((res, rej) =>{
let bal = 0;
let action_counter = 0;
const len = customers.length;
for(let j = 0; j < len; j++) {
fetchOrders(customers[j], '').then((ord) => {
let totalAmount = 0;
let outstandingBalance = 0;
let totalRefunded = 0;
let totalCanceled = 0;
let orderCount = ord.length;
for (let i = 0; i < orderCount; i++) {
let order = ord[i];
totalAmount += order['node']['totalPriceSet']['shopMoney']['amount'];
if (order['node']['displayFinancialStatus'] !== 'PAID') {
outstandingBalance += order['node']['totalPriceSet']['shopMoney']['amount'];
}
if (order['node']['displayFinancialStatus'] !== 'PAID') {
if (order['node']['cancelledAt']) {
totalCanceled += order['node']['totalPriceSet']['shopMoney']['amount'];
} else {
let refundCount = order['node']['refunds'].length;
for (let i = 0; i < refundCount; i++) {
let refund = order['node']['refunds'][i];
totalRefunded += refund['totalRefunded']['amount'];
}
}
}
}
bal += (outstandingBalance - totalRefunded - totalCanceled);
action_counter++
if(len === action_counter){
res(bal);
}
}).catch((err) => {
rej(err)
});
}
})
}
useEffect(()=>{
fetchCustomers();
},[])
useEffect(() => {
if(customers.length === 0){
return;
}
fetchCustomerOrders().then((bal) => {
setBalance(bal);
setDone(true)
}).catch((err)=>{
setError(err);
setDone();
})
}, [customers]);
let ok = false;
let message = '';
if (balance !== -1 && done) {
if ((credit_allowed - balance) >= amount) {
ok = true;
} else {
message = 'You only have ' + (credit_allowed - balance).toFixed(2) + ' available to spend. You can not checkout.';
}
}
const blockResponse = (canBlockProgress) => {
if (!companyData) {
return {
behavior: 'allow',
}
} else if (ok) {
return {
behavior: 'allow',
}
} else if (balance !== -1 && done && message.length > 0) {
return {
behavior: 'block',
reason: 'Not enough credit.',
errors: [
{
message:
message,
},
],
};
} else if (!done && canBlockProgress) {
return {
behavior: 'block',
reason: 'Still calculating ...',
errors: [
{
message:
'Checking if you have any outstanding bills.',
},
],
};
} else {
return {
behavior: 'allow',
}
}
}
const getData = () => {
return 'Balance: ' + balance + ' done: ' + (done ? 'true' : 'false') + 'credit: ' + credit_allowed + ' amount: ' + amount + ' error: ' + error + ' Company: ' + JSON.stringify(companyData) + ' Other: ' + JSON.stringify(otherErrors);
}
const block = blockResponse(true);
let banner = <Banner status="success" title={"You" + getData()} />
if(block['behavior'] === 'block'){
banner = <Banner status="error" title={block['errors'][0]} />
}
useBuyerJourneyIntercept(
({canBlockProgress}) => {
return blockResponse(canBlockProgress)
},
);
return banner;
}
Solved! Go to the solution
This is an accepted solution.
The problem turns out to be that the storefront API (that is used by checkout UI) only has a subset of the Admin API commands. So it is not accessible via the functions above.
I looked in the documentation and apparently "company" exists on query root. So I tried to use it, but it too gives the same typeof error. "Field 'company' doesn't exist on type 'QueryRoot'"
I have the following scopes defined, and have allowed protected customer data.
scopes = "read_customers, read_orders, read_companies"
const fetchCustomers = () => {
query(`query{
company(id: "`+companyData.company.id +`"){
contacts(first: 250){
edges{
node{
customer {
id
}
}
}
}
}
}`).then(({data, errors}) => {
setOtherErrors(errors)
const len = data.contacts.edges.length;
let out = [];
for(let i= 0; i < len; i++){
out.push(data.contacts.edges[i]['node']['id']);
}
setCustomers(out);
}).catch((ex)=>{
setError(ex);
setDone(true);
});
}
This is an accepted solution.
The problem turns out to be that the storefront API (that is used by checkout UI) only has a subset of the Admin API commands. So it is not accessible via the functions above.