I am currently working on a project where I need to print some labels with a button click. However, the labels can only be printed if certain requirements are fulfilled.
First, my user gets to a ShadCn dialog where they can check some checkboxes. The checkboxes then send a request to a function called checkFulfilled (which will be shown further down). That function checks if an item is fulfilled in the order or not. If it isn’t, it sends a POST request to fulfill it. Then, from that function, I pass information to another function called createSale that is needed to book the shipment and mark the fulfillment status in the API. Without that fulfilled status, the label cannot be printed.
Now comes the interesting part. When the order is fulfilled, the API will return the whole order, but only one part of the order is important—that is called order_fulfillments, which can have between 2 to 1000 arrays in it. Every array in the order_fulfillments has a so-called shipment_id, but only one of the shipment_ids is not null. I need to loop through all the shipment IDs before I can print the label because the printLabel API requires me to send in the shipment_id.
The problem: Right now, when I try to print the label, even when everything else has passed successfully, I get this error message: “shipmentId is null, not calling PrintLabel.” Can someone explain how I can solve the problem?
The code:
Dialog component where all frontend interaction happen
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Separator } from "@/components/ui/separator";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Package } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Orders } from "@/lib/types/shipmondo";
import { FurfillOrder, PrintLabel, cancel, createSale, findId, putStatus } from "./actions";
import { Product } from "@/lib/types/wix";
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
import { Checkbox } from "@/components/ui/checkbox";
import { CircleX } from "lucide-react";
import { string } from "zod";
export default function Dialogt({ order, totalWeight, products }: { order: Orders, totalWeight: number, products: Product[] }) {
const router = useRouter();
// const [isOpen, setIsOpen] = useState(true);
const [checkedItems, setCheckedItems] = useState<number[]>([]);
const [isPacking, setIsPacking] = useState(false);
const furfillOrder = async (item_id: string, order_id: string, order: Orders, item_quantity: Number) => {
await FurfillOrder(item_id, order_id, item_quantity);
}
const handleCheckboxChange = (index: number, item_id: string, order_id: string, item_quantity: Number) => {
setCheckedItems(prevCheckedItems =>
prevCheckedItems.includes(index)
? prevCheckedItems.filter(item => item !== index)
: [...prevCheckedItems, index]
);
furfillOrder(item_id, order_id, order, item_quantity);
};
const calculateProgress = () => {
return (checkedItems.length / order.order_lines.length) * 100;
};
// const packad = async (order_id: string) => {
// await putStatus(order_id);
// setIsPacking(true);
// };
// const handleClose = () => {
// setIsOpen(false);
// };
// const cancelOrder = async (order_id: string) => {
// await cancel(order_id);
// handleClose();
// };
const print = async (order_id: any) => {
await PrintLabel(order_id);
}
const ida = async (order_id: string) =>{
await findId(order_id)
}
useEffect(() => {
if (isPacking) {
router.push('/packning/packas');
}
}, [isPacking, router]);
return (
<Dialog>
<DialogTrigger>
<Package size={25} />
</DialogTrigger>
<DialogContent className={'g:max-w-screen-lg overflow-y-scroll max-h-screen'}>
<DialogHeader>
<DialogTitle className="items-center flex justify-around">
Order {order.order_id.substring(0, 12)}
{/* {order.order_status === "on_hold" ? <Badge className="bg-[#ffA500] text-black">Påbörjad</Badge> : <Button onClick={() => packad(order.id)}>Packa</Button>}
{order.order_status === 'on_hold' ? <Button
onClick={() => cancelOrder(order.id)}
variant={'ghost'} color={'destructive'}>Avbryt
</Button> : ''} */}
</DialogTitle>
</DialogHeader>
<DialogDescription>
<Card key={order.id} className="flex items-center p-6 rounded-lg shadow-lg m-4">
<div className="flex flex-col items-start gap-4">
<div className="flex flex-col items-start gap-1">
<h3 className=" font-bold">
{order.ship_to.name}
</h3>
<div className="flex items-center gap-2 text-muted-foreground">
<span>Id: {order.order_id.substring(0, 12)}</span>
<div className="h-3 w-3 rounded-full bg-muted" />
<span>Vikt: {totalWeight.toFixed(2)}kg</span>
</div>
</div>
<div className="flex items-center gap-4">
{order.order_status === "open" ? <Badge variant={'destructive'}>Ska packas</Badge> : <Badge className="bg-[#ffA500] text-black">Påbörjad</Badge>}
<div className="text-base">Artiklar: {`${order.order_lines.length - 1}`}</div>
</div>
</div>
</Card>
{order.order_note && order.order_note.length > 1 ? (
<Card key={order.id} className="flex p-6 rounded-lg shadow-lg m-4">
<h2 className="w-full">
{order.order_note}
</h2>
</Card>
) : (
<Card key={order.id} className="flex p-6 rounded-lg shadow-lg m-4">
<h2 className="w-full">
inga anteckningar
</h2>
</Card>
)}
</DialogDescription>
<Separator />
<DialogHeader>
<DialogTitle className="m-2">Produkter</DialogTitle>
<Progress className="sticky top-4 shadow-2xl" value={calculateProgress()} />
{order.order_lines.map((line: any, index: number) => (
<Card key={index} className="flex items-center p-6 rounded-lg shadow-lg m-4">
<div className="flex flex-col items-start gap-4 w-full">
<div className="flex flex-col items-start gap-1">
<h3 className=" font-bold">
{line.item_name}
</h3>
<div className="flex items-center gap-2 text-muted-foreground">
<span>Id: {line.id}</span>
<div className="h-3 w-3 rounded-full bg-muted" />
<span>Vikt: {line.unit_weight}gr</span>
</div>
</div>
<div className="flex items-center gap-4 font-bold ">
<div>{Math.floor(line.quantity)} st</div>
</div>
</div>
<div className="ml-auto">
<Checkbox
checked={checkedItems.includes(index)}
onClick={() => handleCheckboxChange(index, line.id, order.id, line.quantity)}
/>
</div>
</Card>
))}
{order.fulfillment_status === "fulfilled" || "partially_fulfilled" ?
<Button className="bg-[#00FF00] text-black"
onClick={()=> ida()}>
Printa label
</Button> : ''
}
</DialogHeader>
</DialogContent>
</Dialog>
)
}
The backend where i call all function print, find and etcetera for the api
"use server"
import { Orders } from "@/lib/types/shipmondo"
import { Product } from "@/lib/types/wix";
import { revalidatePath } from 'next/cache'
export const putStatus = async (order_id: string) => {
const url = "https://app.shipmondo.com/api/public/v3/sales_orders/" + order_id;
const options = {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: 'Basic *********=='
},
body: '{"order_status":"on_hold"}'
};
const resp = await fetch(url, options);
const data = await resp.json();
console.log(data.order_status, data.id);
revalidatePath('/packning/packas')
revalidatePath('/packning')
return data
}
// export const cancel = async (order_id: string) => {
// const url = "https://app.shipmondo.com/api/public/v3/sales_orders/" + order_id;
// const options = {
// method: 'PUT',
// headers: {
// 'Content-Type': 'application/json',
// Accept: 'application/json',
// Authorization: 'Basic *********=='
// },
// body: '{"order_status":"open"}'
// };
// const resp = await fetch(url, options);
// const data = await resp.json();
// revalidatePath('/packning/packas')
// revalidatePath('/packning')
// return data
// }
const checkFurfilled = async (order_id: string) => {
const url = `https://app.shipmondo.com/api/public/v3/sales_orders/` + order_id;
const options = {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Basic *******=='
}
}
const response = await fetch(url, options);
const data = await response.json();
await findId(order_id); // <--- added await here
return data;
}
export const createSale = async (order_id: string) => {
const url = `https://app.shipmondo.com/api/public/v3/sales_orders/${order_id}/create_shipment`;
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: 'Basic *********=='
},
}
const fulfillmentStatus = await checkFurfilled(order_id);
console.log(fulfillmentStatus.fulfillment_status)
if (fulfillmentStatus.fulfillment_status === "fulfilled") {
const resp = await fetch(url, options);
const data = await resp.json();
console.log("allt gick genom")
return data;
} else {
console.error("Error: Något gick fel. Order ID:", order_id, "Fulfillment Status:", fulfillmentStatus.fulfillment_status)
}
}
export const FurfillOrder = async (item_id: string, order_id: string, item_quantity: Number) => {
const url = `https://app.shipmondo.com/api/public/v3/sales_orders/${order_id}/fulfillments`;
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: 'Basic *****=='
},
body: `{"fulfillment_lines":[{"order_line_id":${item_id},"shipped_quantity":${item_quantity}}]}`
};
try {
const response = await fetch(url, options);
const data = await response.json();
createSale(order_id);
} catch (error) {
console.error(error);
}
};
export const findId = async (order_id: string) => {
const url = `https://app.shipmondo.com/api/public/v3/sales_orders/` + order_id;
const options = {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Basic *******=='
}
}
const response = await fetch(url, options);
const data = await response.json();
let shipmentId = null;
data?.order_fulfillments?.forEach((fulfillment: any) => {
if (fulfillment.shipment_id !== null) {
shipmentId = fulfillment.shipment_id;
}
});
if (shipmentId !== null) {
console.log('Calling PrintLabel with shipmentId:', shipmentId);
await PrintLabel(shipmentId); // <--- added await here
} else {
console.log('shipmentId is null, not calling PrintLabel');
}
return shipmentId;
};
export const PrintLabel = async (shipmentId: number) => {
const url = 'https://app.shipmondo.com/api/public/v3/print_jobs';
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: 'Basic ******=='
},
body: `{"document_id":${shipmentId},"document_type":"shipment","host_name":"IT50462","printer_name":"ZDesigner GX420d","label_format":"zpl"}`
};
console.log(shipmentId)
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
console.log("due gud")
} catch (error) {
console.error(error);
console.log("något gick fel")
}
}
feel free to ask for further information
NikitaFibr is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.