diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4c13f48..288d06d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -59,7 +59,7 @@ model Location { model Counting { id Int @id @default(autoincrement()) - product_id Int + product_id String product_name String warehouse Int diff --git a/src/app/admin/settings/page.js b/src/app/admin/settings/page.js index 1709436..1ed014e 100644 --- a/src/app/admin/settings/page.js +++ b/src/app/admin/settings/page.js @@ -147,6 +147,36 @@ export default function SettingsPage() { + {/* Enable Counting Modes */} +
+
+
+ +
+

حالت‌های انبارگردانی

+
+ +
+

انبارگردانی قفسه‌ای

+ +
+ +
+

انبارگردانی کالایی

+ +
+
+ {/* Correction Roles Setting */}
diff --git a/src/app/admin/stats/page.js b/src/app/admin/stats/page.js index ac9f19d..8794402 100644 --- a/src/app/admin/stats/page.js +++ b/src/app/admin/stats/page.js @@ -11,7 +11,11 @@ export default function AdminStats() { fetch('/api/admin/stats') .then(res => res.json()) .then(data => { - setStats(data); + if (Array.isArray(data)) { + setStats(data); + } else { + setStats([]); + } setLoading(false); }); }, []); diff --git a/src/app/api/counting/route.js b/src/app/api/counting/route.js index 24cad5f..fc5c4e4 100644 --- a/src/app/api/counting/route.js +++ b/src/app/api/counting/route.js @@ -13,9 +13,9 @@ export async function POST(req) { }); } - const count = await prisma.counting.create({ - data: { - product_id: Number(data.product_id), + const count = await prisma.counting.create({ + data: { + product_id: String(data.product_id), product_name: data.product_name, warehouse: Number(data.warehouse), shelfCode: data.shelfCode ? data.shelfCode.toUpperCase() : null, @@ -43,7 +43,7 @@ export async function GET(req) { try { if (productId && warehouse) { const lastCount = await prisma.counting.findFirst({ - where: { product_id: Number(productId), warehouse: Number(warehouse) }, + where: { product_id: String(productId), warehouse: Number(warehouse) }, orderBy: { createdAt: 'desc' } }); return NextResponse.json(lastCount || { message: -1 }); diff --git a/src/app/api/reports/uncounted/route.js b/src/app/api/reports/uncounted/route.js index 91b607e..edf94e0 100644 --- a/src/app/api/reports/uncounted/route.js +++ b/src/app/api/reports/uncounted/route.js @@ -16,7 +16,7 @@ export async function GET(req) { }); // Get distinct product IDs that were counted - const countedProductIds = new Set(countings.map(c => Number(c.product_id))); + const countedProductIds = new Set(countings.map(c => String(c.product_id))); // 2. Fetch all products from Hesabfa const res = await axios.post('https://api.hesabfa.com/v1/item/getitems', { @@ -31,7 +31,7 @@ export async function GET(req) { // 3. Filter items that have Stock > 0 but are not in countedProductIds const uncounted = hesabfaItems.filter(item => { // Check if not counted - if (countedProductIds.has(item.Code)) return false; + if (countedProductIds.has(String(item.Code))) return false; // Check if it has stock (assuming item.Stock or finding via another property, some accounts use item.Stock) // Since getitems might not return Stock per warehouse, we assume if Stock > 0 it should have been counted. diff --git a/src/app/counting/item/page.js b/src/app/counting/item/page.js index 76f7645..f41410c 100644 --- a/src/app/counting/item/page.js +++ b/src/app/counting/item/page.js @@ -1,8 +1,8 @@ 'use client'; -import { useState, useEffect, Suspense } from 'react'; +import { useState, useEffect, Suspense, useRef } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import Header from '@/components/Header'; -import { Box, Layers, CheckCircle2, ScanLine, X, AlertCircle } from 'lucide-react'; +import { ScanLine, Search, Check, Box, Layers, AlertCircle, X, History } from 'lucide-react'; import { hasRole } from '@/lib/auth'; import { saveCountOffline, syncOfflineCounts } from '@/lib/offlineSync'; import dynamic from 'next/dynamic'; @@ -13,18 +13,19 @@ const Scanner = dynamic(() => import('@yudiel/react-qr-scanner').then(mod => mod function ItemCountingContent() { const router = useRouter(); const searchParams = useSearchParams(); - const code = searchParams.get('code'); + const product_id = searchParams.get('product_id'); const warehouse = searchParams.get('warehouse'); const [user, setUser] = useState(null); const [settings, setSettings] = useState({}); const [loading, setLoading] = useState(true); + // We fetch product details based on product_id from query const [productName, setProductName] = useState(''); const [oldCount, setOldCount] = useState(null); - - const [shelfCode, setShelfCode] = useState(''); const [newCount, setNewCount] = useState(''); + const [shelfCode, setShelfCode] = useState(''); + const [submitLoading, setSubmitLoading] = useState(false); const [history, setHistory] = useState([]); @@ -32,44 +33,69 @@ function ItemCountingContent() { const [camError, setCamError] = useState(''); const [errorMsg, setErrorMsg] = useState(''); + const inputRef = useRef(null); + useEffect(() => { const userData = localStorage.getItem('user'); if (userData) setUser(JSON.parse(userData)); - fetchSettingsAndData(); + fetchSettings(); + fetchInitialItemData(); window.addEventListener('online', syncOfflineCounts); return () => window.removeEventListener('online', syncOfflineCounts); - }, [code, warehouse]); + }, []); - const fetchSettingsAndData = async () => { + const fetchSettings = async () => { try { - const setRes = await fetch('/api/settings'); - if (setRes.ok) { - const data = await setRes.json(); - setSettings(data); + const res = await fetch('/api/settings'); + if (res.ok) setSettings(await res.json()); + } catch (e) {} + }; + + const fetchInitialItemData = async () => { + if (!product_id) { + router.push('/dashboard'); + return; + } + + try { + const nameRes = await fetch('/api/hesabfa', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ code: product_id, type: 'name' }) + }); + const nameData = await nameRes.json(); + + if (!nameData?.Result?.Name || nameData?.Result?.Name === 'نامشخص') { + setErrorMsg('کالا یافت نشد.'); + } else { + setProductName(nameData.Result.Name); } - if (code) { - const nameRes = await fetch('/api/hesabfa', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ code, type: 'name' }) - }); - const nameData = await nameRes.json(); - setProductName(nameData?.Result?.Name || 'نامشخص'); - - const qRes = await fetch('/api/hesabfa', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ code, type: 'quantity' }) - }); - const qData = await qRes.json(); - const productInfo = qData?.Result?.[0]; - const wInfo = productInfo?.Warehouse?.find(w => w.Code === Number(warehouse)); - setOldCount(wInfo?.Quantity ?? 0); + const qRes = await fetch('/api/hesabfa', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ code: product_id, type: 'quantity' }) + }); + const qData = await qRes.json(); + const productInfo = qData?.Result?.[0]; + + let foundStock = 0; + if (productInfo) { + if (productInfo.StockByWarehouse && Array.isArray(productInfo.StockByWarehouse)) { + const wInfo = productInfo.StockByWarehouse.find(w => w.WarehouseCode === Number(warehouse) || w.Code === Number(warehouse)); + if (wInfo) foundStock = wInfo.Stock || wInfo.Quantity || 0; + else foundStock = productInfo.Stock || productInfo.Quantity || 0; + } else if (productInfo.Warehouse && Array.isArray(productInfo.Warehouse)) { + const wInfo = productInfo.Warehouse.find(w => w.Code === Number(warehouse)); + foundStock = wInfo?.Quantity ?? productInfo.Stock ?? productInfo.Quantity ?? 0; + } else { + foundStock = productInfo.Stock ?? productInfo.Quantity ?? 0; + } } - } catch (e) { - console.error(e); + setOldCount(foundStock); + } catch (error) { + setErrorMsg('خطا در دریافت اطلاعات کالا از سرور'); } finally { setLoading(false); } @@ -78,61 +104,44 @@ function ItemCountingContent() { const handleScan = (detectedCodes) => { if (detectedCodes && detectedCodes.length > 0) { const scannedValue = detectedCodes[0].rawValue; - setShelfCode(scannedValue.toUpperCase()); - setCameraEnabled(false); - setErrorMsg(''); + // We do not close the camera! + setShelfCode(scannedValue); + setCamError(''); + if (inputRef.current) setTimeout(() => inputRef.current.focus(), 100); } }; const handleError = (error) => { const msg = error?.message || error?.name || ''; - if (msg.includes('Requested device not found')) { - setCamError('دوربینی یافت نشد.'); - } else { - setCamError('خطا در دسترسی به دوربین.'); - } + if (msg.includes('Requested device not found')) setCamError('دوربینی یافت نشد.'); + else setCamError('خطا در دسترسی به دوربین.'); }; - const validateShelfAndSubmit = async () => { - setErrorMsg(''); + const handleSubmitShelf = async () => { if (!shelfCode) { - setErrorMsg('لطفاً کد قفسه را وارد یا اسکن کنید'); + setErrorMsg('کد قفسه را وارد یا اسکن کنید'); return; } if (newCount === '' || newCount === null) { - setErrorMsg('لطفاً تعداد را وارد کنید'); + setErrorMsg('تعداد را وارد کنید'); return; } setSubmitLoading(true); + setErrorMsg(''); + + const payload = { + product_id: String(product_id), + product_name: productName, + warehouse: Number(warehouse), + shelfCode: shelfCode.toUpperCase(), + old_count: oldCount || 0, + new_count: Number(newCount), + user_id: user?.id, + mode: 'ITEM' + }; try { - // 1. Validate shelf - const valRes = await fetch('/api/locations/validate', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ code: shelfCode.toUpperCase(), warehouse }) - }); - const valData = await valRes.json(); - - if (!valRes.ok) { - setErrorMsg(valData.error || 'قفسه نامعتبر است'); - setSubmitLoading(false); - return; - } - - // 2. Submit count - const payload = { - product_id: code, - product_name: productName, - warehouse, - shelfCode: shelfCode.toUpperCase(), - old_count: oldCount || 0, - new_count: Number(newCount), - user_id: user?.id, - mode: 'ITEM' - }; - const res = await fetch('/api/counting', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -143,23 +152,14 @@ function ItemCountingContent() { setHistory([{ shelf: shelfCode.toUpperCase(), count: newCount }, ...history]); setShelfCode(''); setNewCount(''); + setErrorMsg(''); } else { - setErrorMsg('خطا در ثبت شمارش در سرور'); + const data = await res.json(); + setErrorMsg(data.error || 'خطا در ثبت اطلاعات در سرور'); } } catch (err) { - // 3. Fallback offline - const payload = { - product_id: code, - product_name: productName, - warehouse, - shelfCode: shelfCode.toUpperCase(), - old_count: oldCount || 0, - new_count: Number(newCount), - user_id: user?.id, - mode: 'ITEM' - }; await saveCountOffline(payload); - alert('ارتباط با سرور قطع است. اطلاعات موقتاً در گوشی شما ذخیره شد.'); + alert('ارتباط با سرور قطع است. اطلاعات ذخیره شد.'); setHistory([{ shelf: shelfCode.toUpperCase(), count: newCount, offline: true }, ...history]); setShelfCode(''); setNewCount(''); @@ -176,18 +176,14 @@ function ItemCountingContent() { await fetch('/api/counting/cancel', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ product_id: code, warehouse, userId: user?.id, reason, mode: 'ITEM' }) + body: JSON.stringify({ product_id, warehouse, userId: user?.id, reason, mode: 'ITEM' }) }); router.push('/dashboard'); } catch (error) { - alert('خطا در لغو انبارگردانی کالا'); + alert('خطا در ارتباط با سرور'); } }; - const handleFinish = () => { - router.push('/dashboard'); - }; - const isBlind = settings?.blind_counting && !hasRole(user?.roles, ['ADMIN', 'SUPERVISOR']); if (loading) { @@ -205,148 +201,151 @@ function ItemCountingContent() {
- {/* Top Banner */} -
-
- -
-
-

{productName}

-

کد: {code}

-
- {!isBlind && oldCount !== null && ( -
- {oldCount} - موجودی سیستم + {/* Top Fixed Info Bar */} +
+
+
+
- )} +
+ {productName} + آماده ثبت قفسه +
+
+ +
+ + +
-
+
- {/* Input Form */} -
-
- -

- - ثبت شمارش در قفسه -

- - {errorMsg && ( -
- - {errorMsg} -
- )} - -
-
- { setShelfCode(e.target.value); setErrorMsg(''); }} - placeholder="مثال: C-1" - className="flex-1 py-3.5 bg-gray-50 border border-gray-200 rounded-[16px] text-center text-lg font-black text-gray-800 uppercase focus:outline-none focus:border-indigo-500 focus:bg-white transition-all placeholder:text-sm placeholder:font-normal placeholder:text-gray-400" - /> - setCameraEnabled(true)} - className="w-14 bg-indigo-50 text-indigo-600 rounded-[16px] flex items-center justify-center shrink-0 hover:bg-indigo-100 transition-colors border border-indigo-100" - > - - -
- - - {cameraEnabled && ( - - {camError ? ( -
{camError}
- ) : ( -
- -
- )} - -
- )} -
+ {!isBlind && oldCount !== null && ( +
+ موجودی کل در سیستم: + {oldCount} +
+ )} + {/* Continuous Scanner Area for Shelf */} +
+
+ {camError ? ( +
{camError}
+ ) : cameraEnabled ? ( + <> +
+ +
+ {/* Scanner Overlay UI */} +
+
+
+ + ) : ( + + )} +
+ +
{ setShelfCode(e.target.value); setErrorMsg(''); }} + placeholder="کد قفسه..." + className="flex-1 bg-gray-50 border border-gray-200 rounded-[14px] px-4 py-3 text-sm font-bold text-gray-800 uppercase focus:outline-none focus:border-teal-500 text-center placeholder:font-normal placeholder:normal-case" + /> +
+
+ + {/* Error Message */} + + {errorMsg && ( + + + {errorMsg} + + )} + + + {/* Count Input Box */} +
+
+ +
+

+ + ثبت موجودی در قفسه {shelfCode ? `«${shelfCode.toUpperCase()}»` : ''} +

+
+ +
+ { setNewCount(e.target.value); setErrorMsg(''); }} - placeholder="تعداد یافت شده در این قفسه..." - className="w-full border-2 border-gray-200 bg-white rounded-[16px] p-4 text-center text-2xl font-black text-gray-800 focus:outline-none focus:border-indigo-500 transition-all placeholder:text-sm placeholder:font-medium placeholder:text-gray-300" + placeholder="0" + className="w-full bg-gray-50 border-2 border-gray-200 rounded-[16px] p-4 text-center text-3xl font-black text-gray-900 focus:outline-none focus:border-teal-500 focus:bg-white transition-all placeholder:text-gray-300" /> -
- {/* History of this item in different shelves */} + {/* History of this session */} {history.length > 0 && ( -
-

مکان‌های ثبت شده شما

-
+
+

+ + قفسه‌های شمارش شده برای این کالا +

+
{history.map((item, idx) => ( -
+
+ {item.offline &&
}
-
- +
+
- {item.shelf} - {item.offline && ثبت آفلاین} +

{item.shelf}

-
- شمارش شده - {item.count} +
+ {item.count}
))}
)} - -
- - -
diff --git a/src/app/counting/shelf/page.js b/src/app/counting/shelf/page.js index 3ef2c45..e1fa2ff 100644 --- a/src/app/counting/shelf/page.js +++ b/src/app/counting/shelf/page.js @@ -1,8 +1,8 @@ 'use client'; -import { useState, useEffect, Suspense } from 'react'; +import { useState, useEffect, Suspense, useRef } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import Header from '@/components/Header'; -import { Lock, Unlock, ScanLine, Search, Check, Box, Layers, AlertCircle, X } from 'lucide-react'; +import { Lock, Unlock, ScanLine, Search, Check, Box, Layers, AlertCircle, X, History } from 'lucide-react'; import { hasRole } from '@/lib/auth'; import { saveCountOffline, syncOfflineCounts } from '@/lib/offlineSync'; import dynamic from 'next/dynamic'; @@ -20,7 +20,6 @@ function ShelfCountingContent() { const [settings, setSettings] = useState({}); const [loading, setLoading] = useState(true); - // Item scanning state const [productCode, setProductCode] = useState(''); const [productName, setProductName] = useState(''); const [oldCount, setOldCount] = useState(null); @@ -33,8 +32,9 @@ function ShelfCountingContent() { const [cameraEnabled, setCameraEnabled] = useState(true); const [camError, setCamError] = useState(''); - // History of scanned items in this session const [history, setHistory] = useState([]); + + const inputRef = useRef(null); useEffect(() => { const userData = localStorage.getItem('user'); @@ -52,11 +52,7 @@ function ShelfCountingContent() { const data = await res.json(); setSettings(data); } - } catch (e) { - console.error(e); - } finally { - setLoading(false); - } + } catch (e) {} finally { setLoading(false); } }; const handleFinishShelf = async () => { @@ -76,7 +72,7 @@ function ShelfCountingContent() { const fetchItemData = async (codeToFetch) => { const code = codeToFetch || productCode; if (!code) { - setErrorMsg('کد کالا را وارد کنید'); + setErrorMsg('لطفاً کد کالا را وارد کنید'); return; } @@ -85,6 +81,7 @@ function ShelfCountingContent() { setProductName(''); setOldCount(null); setNewCount(''); + setProductCode(code); try { const nameRes = await fetch('/api/hesabfa', { @@ -95,7 +92,7 @@ function ShelfCountingContent() { const nameData = await nameRes.json(); if (!nameData?.Result?.Name || nameData?.Result?.Name === 'نامشخص' || nameData.error) { - setErrorMsg('کالایی با این کد در حسابفا یافت نشد.'); + setErrorMsg(`کالایی با کد ${code} در حسابفا یافت نشد.`); setItemLoading(false); return; } @@ -109,22 +106,33 @@ function ShelfCountingContent() { }); const qData = await qRes.json(); const productInfo = qData?.Result?.[0]; - const wInfo = productInfo?.Warehouse?.find(w => w.Code === Number(warehouse)); - setOldCount(wInfo?.Quantity ?? 0); + + let foundStock = 0; + if (productInfo) { + if (productInfo.StockByWarehouse && Array.isArray(productInfo.StockByWarehouse)) { + const wInfo = productInfo.StockByWarehouse.find(w => w.WarehouseCode === Number(warehouse) || w.Code === Number(warehouse)); + if (wInfo) foundStock = wInfo.Stock || wInfo.Quantity || 0; + else foundStock = productInfo.Stock || productInfo.Quantity || 0; + } else if (productInfo.Warehouse && Array.isArray(productInfo.Warehouse)) { + const wInfo = productInfo.Warehouse.find(w => w.Code === Number(warehouse)); + foundStock = wInfo?.Quantity ?? productInfo.Stock ?? productInfo.Quantity ?? 0; + } else { + foundStock = productInfo.Stock ?? productInfo.Quantity ?? 0; + } + } + setOldCount(foundStock); + + if (inputRef.current) { + setTimeout(() => inputRef.current.focus(), 100); + } } catch (error) { console.error(error); - setErrorMsg('خطا در ارتباط با حسابفا'); + setErrorMsg('خطا در ارتباط با حسابفا. لطفاً دوباره تلاش کنید.'); } finally { setItemLoading(false); } }; - const handleProductCodeChange = (e) => { - setProductCode(e.target.value); - setErrorMsg(''); - setProductName(''); - }; - const handleProductCodeKeyDown = (e) => { if (e.key === 'Enter') { fetchItemData(); @@ -134,20 +142,18 @@ function ShelfCountingContent() { const handleScan = (detectedCodes) => { if (detectedCodes && detectedCodes.length > 0) { const scannedValue = detectedCodes[0].rawValue; - setProductCode(scannedValue); - setCameraEnabled(false); + // We DO NOT turn off the camera. Just fetch! setCamError(''); - fetchItemData(scannedValue); + if (scannedValue !== productCode) { + fetchItemData(scannedValue); + } } }; const handleError = (error) => { const msg = error?.message || error?.name || ''; - if (msg.includes('Requested device not found')) { - setCamError('دوربینی یافت نشد.'); - } else { - setCamError('خطا در دسترسی به دوربین.'); - } + if (msg.includes('Requested device not found')) setCamError('دوربینی یافت نشد.'); + else setCamError('خطا در دسترسی به دوربین.'); }; const handleSubmitItem = async () => { @@ -163,9 +169,9 @@ function ShelfCountingContent() { setSubmitLoading(true); const payload = { - product_id: productCode, + product_id: String(productCode), product_name: productName, - warehouse, + warehouse: Number(warehouse), shelfCode: shelfCode.toUpperCase(), old_count: oldCount || 0, new_count: Number(newCount), @@ -188,12 +194,11 @@ function ShelfCountingContent() { setNewCount(''); setErrorMsg(''); } else { - setErrorMsg('خطا در ثبت شمارش کالا در سرور'); + setErrorMsg('خطا در ثبت شمارش در سرور'); } } catch (err) { - // Network error (offline or server down) await saveCountOffline(payload); - alert('ارتباط با سرور قطع است. اطلاعات در گوشی شما ذخیره شد و به محض اتصال ارسال می‌شود.'); + alert('ارتباط با سرور قطع است. شمارش ذخیره شد و پس از اتصال ارسال می‌شود.'); setHistory([{ code: productCode, name: productName, count: newCount, offline: true }, ...history]); setProductCode(''); setProductName(''); @@ -238,184 +243,161 @@ function ShelfCountingContent() {
- {/* Top Banner */} -
+ {/* Top Fixed Info Bar */} +
-
- +
+ {shelfCode}
- در حال شمارش قفسه - {shelfCode} + قفسه جاری + آماده اسکن کالای بعدی
-
+
- {/* Scanner Form */} -
-
+ {/* Continuous Scanner Area */} +
+
+ {camError ? ( +
{camError}
+ ) : cameraEnabled ? ( + <> +
+ +
+ {/* Scanner Overlay UI */} +
+
+
+ + ) : ( + + )} +
-

- - اسکن کالای جدید در این قفسه -

- - {errorMsg && ( -
- - {errorMsg} -
- )} - -
-
- - fetchItemData()} - disabled={itemLoading || !productCode} - className="w-14 bg-indigo-50 text-indigo-600 rounded-[16px] flex items-center justify-center shrink-0 hover:bg-indigo-100 transition-colors border border-indigo-100 disabled:opacity-50" - > - {itemLoading ?
: } -
- setCameraEnabled(true)} - className="w-14 bg-gray-900 text-white rounded-[16px] flex items-center justify-center shrink-0 shadow-md transition-colors" - > - - -
- - - {cameraEnabled && ( - - {camError ? ( -
{camError}
- ) : ( -
- -
- )} - -
- )} -
- - {/* Product Details & Count input (only shown if valid product found) */} - - {productName && !itemLoading && ( - -
-
-
- -
-
-

{productName}

-

کد حسابفا: {productCode}

-
-
- {!isBlind && oldCount !== null && ( -
- {oldCount} - موجودی سیستم -
- )} -
- -
- { setNewCount(e.target.value); setErrorMsg(''); }} - placeholder="تعداد شمارش شده را وارد کنید..." - className="w-full border-2 border-gray-200 bg-white rounded-[16px] p-4 text-center text-2xl font-black text-gray-800 focus:outline-none focus:border-indigo-500 transition-all placeholder:text-sm placeholder:font-medium placeholder:text-gray-300" - /> - -
-
- )} -
+
+ { setProductCode(e.target.value); setErrorMsg(''); setProductName(''); }} + onKeyDown={handleProductCodeKeyDown} + placeholder="یا بارکد را اینجا تایپ کنید..." + className="flex-1 bg-gray-50 border border-gray-200 rounded-[14px] px-4 py-3 text-sm font-bold text-gray-800 focus:outline-none focus:border-indigo-500 text-center placeholder:font-normal" + /> +
+ {/* Error Message */} + + {errorMsg && ( + + + {errorMsg} + + )} + + + {/* Product Details & Count input (only shown if valid product found) */} + + {productName && !itemLoading && ( + +
+ +
+
+

{productName}

+

کد حسابفا: {productCode}

+
+ {!isBlind && oldCount !== null && ( +
+ {oldCount} + موجودی سیستم +
+ )} +
+ +
+ { setNewCount(e.target.value); setErrorMsg(''); }} + placeholder="0" + className="w-full bg-gray-50 border-2 border-gray-200 rounded-[16px] p-4 text-center text-3xl font-black text-gray-900 focus:outline-none focus:border-indigo-500 focus:bg-white transition-all placeholder:text-gray-300" + /> + +
+
+ )} +
+ {/* History of this session */} {history.length > 0 && ( -
-

کالاهای ثبت شده تا الان

-
+
+

+ + تاریخچه شمارش‌های شما در این قفسه +

+
{history.map((item, idx) => ( -
- {item.offline && ( -
- )} +
+ {item.offline &&
}
-
- +
+

{item.name}

-

کد: {item.code}

+

کد: {item.code}

-
- شمارش شما - - {item.count} - +
+ {item.count}
))} diff --git a/src/app/history/page.js b/src/app/history/page.js index 3b99524..7be5fa6 100644 --- a/src/app/history/page.js +++ b/src/app/history/page.js @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import Header from '@/components/Header'; import { hasRole } from '@/lib/auth'; -import { Edit2, Check, X, Box, Layers, User, Save } from 'lucide-react'; +import { Edit2, Check, X, Box, Layers, User, Save, History } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; export default function HistoryPage() { diff --git a/src/app/layout.js b/src/app/layout.js index 4e03c13..447150d 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -30,7 +30,7 @@ export default function RootLayout({ children }) {

- Designed & Developed by DrMesta + HOUKA

App v1.4.0

diff --git a/src/app/scan/page.js b/src/app/scan/page.js index 9f84a03..fa9b4f9 100644 --- a/src/app/scan/page.js +++ b/src/app/scan/page.js @@ -16,18 +16,26 @@ export default function ScanPage() { const [loading, setLoading] = useState(false); const [lockError, setLockError] = useState(''); const [warehouses, setWarehouses] = useState([]); + const [settings, setSettings] = useState(null); const router = useRouter(); useEffect(() => { fetch('/api/settings') .then(res => res.json()) .then(data => { + setSettings(data); if (data.warehouses) { setWarehouses(data.warehouses); if (data.warehouses.length > 0) { setWarehouse(data.warehouses[0].id); } } + + const shelfEnabled = data.enable_shelf_counting ?? true; + const itemEnabled = data.enable_item_counting ?? true; + + if (shelfEnabled && !itemEnabled) setMode('SHELF'); + if (!shelfEnabled && itemEnabled) setMode('ITEM'); }); }, []); @@ -90,20 +98,29 @@ export default function ScanPage() { {/* Mode Switcher */}
- - + {(settings?.enable_item_counting ?? true) && ( + + )} + {(settings?.enable_shelf_counting ?? true) && ( + + )} + {!(settings?.enable_item_counting ?? true) && !(settings?.enable_shelf_counting ?? true) && ( +
+ تمامی حالت‌های انبارگردانی غیرفعال شده‌اند +
+ )}
{/* Camera Area */} diff --git a/testHesabfa.js b/testHesabfa.js new file mode 100644 index 0000000..cd07e15 --- /dev/null +++ b/testHesabfa.js @@ -0,0 +1,34 @@ +const axios = require('axios'); + +async function test() { + const HESABFA_API_KEY = 'NCuDX3bksHlhXWGIqTvatvme3YTplxdF'; + const HESABFA_TOKEN = '4ddb2fc517f6f6fe6d4b9bdd08fa0df31a564a62e12c4353eb9533ae63447b57ca87c479beb7f02b276929c861dad779'; + + try { + const res = await axios.post('https://api.hesabfa.com/v1/item/getitems', { + apiKey: HESABFA_API_KEY, + loginToken: HESABFA_TOKEN, + queryInfo: { Take: 5, Skip: 0 }, + type: 0 + }); + console.log("getitems List:"); + console.log(JSON.stringify(res.data.Result.List, null, 2)); + + if (res.data.Result.List.length > 0) { + const code = res.data.Result.List[0].Code; + console.log("Checking quantity for:", code); + const qRes = await axios.post('https://api.hesabfa.com/v1/item/GetQuantity2', { + apiKey: HESABFA_API_KEY, + loginToken: HESABFA_TOKEN, + codes: [code] + }); + console.log("GetQuantity2:"); + console.log(JSON.stringify(qRes.data, null, 2)); + } + process.exit(0); + } catch(e) { + console.error(e.response ? e.response.data : e.message); + } +} + +test();