3import { useState, useEffect, Suspense } from 'react';
4import { useSearchParams, useRouter } from 'next/navigation';
7 f1100: string; // Line ID
8 f1101: string; // Product ID
9 f1102: string; // Description
10 f1103: number; // Quantity Ordered
11 f1104: number; // Quantity Received
12 f1105: number; // Unit Cost
13 f1106: number; // Line Total
14 f1107: string; // Product Code
15 f1108: number; // Quantity Outstanding
18interface PurchaseOrder {
19 f100: string; // PO ID
20 f101: string; // Supplier ID
21 f102: string; // Supplier Name
22 f103: string; // PO Date
23 f104: number; // Total Amount
24 f114: number; // Status
25 f132: string; // Notes
30function ReceivePurchaseOrderContent() {
31 const searchParams = useSearchParams();
32 const router = useRouter();
33 const poId = searchParams.get('poid');
34 const physKey = searchParams.get('physkey');
36 const [purchaseOrder, setPurchaseOrder] = useState<PurchaseOrder | null>(null);
37 const [lineItems, setLineItems] = useState<POLineItem[]>([]);
38 const [receivingQuantities, setReceivingQuantities] = useState<Record<string, number>>({});
39 const [receiveDate, setReceiveDate] = useState(new Date().toISOString().split('T')[0]);
40 const [notes, setNotes] = useState('');
41 const [showScanMode, setShowScanMode] = useState(false);
42 const [scanInput, setScanInput] = useState('');
43 const [isLoading, setIsLoading] = useState(true);
46 if (poId && physKey) {
51 const loadPurchaseOrder = async () => {
54 // Mock data - replace with actual API call
55 const mockPO: PurchaseOrder = {
58 f102: 'ABC Suppliers Ltd',
62 f132: 'Urgent delivery required',
63 physkey: physKey || '',
68 f1102: 'Premium Coffee Beans 1kg',
79 f1102: 'Organic Tea Selection Box',
90 f1102: 'Stainless Steel Teapot',
101 f1102: 'Coffee Grinder Professional',
112 f1102: 'Paper Cups 100pk',
123 setPurchaseOrder(mockPO);
124 setLineItems(mockPO.lines);
126 // Initialize receiving quantities with outstanding amounts
127 const initialQuantities: Record<string, number> = {};
128 mockPO.lines.forEach(line => {
129 initialQuantities[line.f1100] = line.f1108; // Set to outstanding quantity
131 setReceivingQuantities(initialQuantities);
134 console.error('Error loading purchase order:', error);
135 alert('Failed to load purchase order');
141 const handleQuantityChange = (lineId: string, value: string) => {
142 const numValue = parseInt(value) || 0;
143 setReceivingQuantities(prev => ({
149 const handleReceiveAll = () => {
150 const allQuantities: Record<string, number> = {};
151 lineItems.forEach(line => {
152 allQuantities[line.f1100] = line.f1108; // Set all to outstanding
154 setReceivingQuantities(allQuantities);
157 const handleClearAll = () => {
158 const cleared: Record<string, number> = {};
159 lineItems.forEach(line => {
160 cleared[line.f1100] = 0;
162 setReceivingQuantities(cleared);
165 const handleScanSubmit = (e: React.FormEvent) => {
167 if (!scanInput.trim()) return;
169 // Find matching product by code
170 const matchingLine = lineItems.find(line =>
171 line.f1107.toLowerCase() === scanInput.toLowerCase() ||
172 line.f1101.toLowerCase() === scanInput.toLowerCase()
176 setReceivingQuantities(prev => ({
178 [matchingLine.f1100]: (prev[matchingLine.f1100] || 0) + 1
182 alert(`Product not found: ${scanInput}`);
186 const handleSubmitReceipt = async () => {
187 const receivingLines = Object.entries(receivingQuantities)
188 .filter(([_, qty]) => qty > 0)
189 .map(([lineId, qty]) => {
190 const line = lineItems.find(l => l.f1100 === lineId);
193 productId: line?.f1101,
198 if (receivingLines.length === 0) {
199 alert('Please enter quantities to receive');
203 console.log('Submitting receipt with Fieldpine DATI packet:');
205 request: 'save_data',
206 client: 'everydaypos-web',
211 f100_E: purchaseOrder?.f100, // PO ID
212 f200_E: receiveDate, // Receipt Date
213 f201_s: notes, // Notes
214 lines: receivingLines.map(line => ({
215 f1100_E: line.lineId, // Line ID
216 f1101_E: line.productId, // Product ID
217 f1200_N: line.receivedQty // Received Quantity
222 console.log(JSON.stringify(datiPacket, null, 2));
225 if (confirm(`Receive ${receivingLines.length} line item(s) for PO ${purchaseOrder?.f100}?`)) {
226 alert('Receipt processed successfully! (This would call Fieldpine API in production)');
227 router.push('/purchase-orders');
233 <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 overflow-auto">
234 <div className="container mx-auto px-4 py-8">
235 <div className="text-center">Loading purchase order...</div>
241 if (!purchaseOrder) {
243 <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 overflow-auto">
244 <div className="container mx-auto px-4 py-8">
245 <div className="text-center text-red-600">Purchase order not found</div>
251 const totalReceiving = Object.values(receivingQuantities).reduce((sum, qty) => sum + qty, 0);
252 const totalOutstanding = lineItems.reduce((sum, line) => sum + line.f1108, 0);
255 <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 overflow-auto">
256 <div className="container mx-auto px-4 py-8">
258 <div className="mb-6">
259 <div className="flex items-center justify-between mb-2">
260 <h1 className="text-3xl font-bold text-slate-800">Receive Purchase Order</h1>
262 onClick={() => router.push('/purchase-orders')}
263 className="px-4 py-2 text-slate-600 hover:text-slate-800"
268 <p className="text-slate-600">PO #{purchaseOrder.f100} - {purchaseOrder.f102}</p>
271 {/* PO Summary Card */}
272 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
273 <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
275 <div className="text-sm text-slate-500">PO Date</div>
276 <div className="font-semibold text-slate-800">{purchaseOrder.f103}</div>
279 <div className="text-sm text-slate-500">Total Amount</div>
280 <div className="font-semibold text-slate-800">${purchaseOrder.f104.toFixed(2)}</div>
283 <div className="text-sm text-slate-500">Total Outstanding</div>
284 <div className="font-semibold text-amber-600">{totalOutstanding} units</div>
287 <div className="text-sm text-slate-500">Now Receiving</div>
288 <div className="font-semibold text-[#00946b]">{totalReceiving} units</div>
291 {purchaseOrder.f132 && (
292 <div className="mt-4 pt-4 border-t border-slate-200">
293 <div className="text-sm text-slate-500">Notes</div>
294 <div className="text-slate-700">{purchaseOrder.f132}</div>
300 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-4 mb-6">
301 <div className="flex flex-wrap items-center gap-4">
302 <div className="flex items-center gap-2">
303 <label className="text-sm font-medium text-slate-700">Receive Date:</label>
307 onChange={(e) => setReceiveDate(e.target.value)}
308 className="px-3 py-2 border border-slate-300 rounded-lg text-sm"
312 <div className="flex items-center gap-2 ml-auto">
314 onClick={() => setShowScanMode(!showScanMode)}
315 className="px-4 py-2 bg-slate-600 text-white rounded-lg hover:bg-slate-700 text-sm"
317 {showScanMode ? '⌨️ Manual' : '📷 Scan Mode'}
320 onClick={handleReceiveAll}
321 className="px-4 py-2 bg-[#00946b] text-white rounded-lg hover:opacity-80 text-sm"
326 onClick={handleClearAll}
327 className="px-4 py-2 bg-slate-400 text-white rounded-lg hover:bg-slate-500 text-sm"
335 <form onSubmit={handleScanSubmit} className="mt-4 pt-4 border-t border-slate-200">
336 <div className="flex items-center gap-2">
337 <label className="text-sm font-medium text-slate-700">Scan Product:</label>
341 onChange={(e) => setScanInput(e.target.value)}
342 placeholder="Scan barcode or enter product code"
343 className="flex-1 px-3 py-2 border border-slate-300 rounded-lg text-sm"
348 className="px-4 py-2 bg-[#00946b] text-white rounded-lg hover:opacity-80 text-sm"
357 {/* Line Items Table */}
358 <div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden mb-6">
359 <div className="overflow-x-auto">
360 <table className="w-full">
361 <thead className="bg-slate-50 border-b border-slate-200">
363 <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">Code</th>
364 <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">Description</th>
365 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Ordered</th>
366 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Already Received</th>
367 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Outstanding</th>
368 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Receiving Now</th>
369 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Unit Cost</th>
370 <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">Line Total</th>
373 <tbody className="divide-y divide-slate-200">
374 {lineItems.map((line) => {
375 const receivingQty = receivingQuantities[line.f1100] || 0;
376 const lineTotal = receivingQty * line.f1105;
379 <tr key={line.f1100} className="hover:bg-slate-50">
380 <td className="px-4 py-3 text-sm text-slate-800 font-medium">{line.f1107}</td>
381 <td className="px-4 py-3 text-sm text-slate-700">{line.f1102}</td>
382 <td className="px-4 py-3 text-sm text-slate-700 text-right">{line.f1103}</td>
383 <td className="px-4 py-3 text-sm text-slate-700 text-right">{line.f1104}</td>
384 <td className="px-4 py-3 text-sm text-amber-600 font-semibold text-right">{line.f1108}</td>
385 <td className="px-4 py-3 text-right">
391 onChange={(e) => handleQuantityChange(line.f1100, e.target.value)}
392 className="w-20 px-2 py-1 border border-slate-300 rounded text-sm text-right"
395 <td className="px-4 py-3 text-sm text-slate-700 text-right">${line.f1105.toFixed(2)}</td>
396 <td className="px-4 py-3 text-sm text-slate-800 font-semibold text-right">
397 ${lineTotal.toFixed(2)}
407 {/* Receipt Notes */}
408 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
409 <label className="block text-sm font-medium text-slate-700 mb-2">Receipt Notes</label>
412 onChange={(e) => setNotes(e.target.value)}
413 placeholder="Add any notes about this receipt (optional)"
415 className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm"
419 {/* Submit Section */}
420 <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
421 <div className="flex items-center justify-between">
423 <div className="text-sm text-slate-500">Total Receiving</div>
424 <div className="text-2xl font-bold text-[#00946b]">{totalReceiving} units</div>
426 <div className="flex gap-3">
428 onClick={() => router.push('/purchase-orders')}
429 className="px-6 py-3 bg-slate-300 text-slate-700 rounded-lg hover:bg-slate-400 font-semibold"
434 onClick={handleSubmitReceipt}
435 disabled={totalReceiving === 0}
436 className="px-6 py-3 bg-[#00946b] text-white rounded-lg hover:opacity-80 font-semibold disabled:opacity-50 disabled:cursor-not-allowed"
448export default function ReceivePurchaseOrder() {
450 <Suspense fallback={<div className="flex items-center justify-center min-h-screen">Loading...</div>}>
451 <ReceivePurchaseOrderContent />