Files
accounting-frontend/src/pages/sales/SalesProcess.tsx
Shayan Azadi f6b6e27294 Add FunZone sync for sales and purchases, voucher filters, and event-name support.
Manual sync buttons pull customer payments and owner payouts into treasury, invoices, and accounting vouchers; voucher page gains category, date, status, and search filters; supplier vouchers dedupe against treasury-backed entries.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 22:09:08 +03:30

119 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Link } from 'react-router-dom'
import { useMemo } from 'react'
import { useStore } from '../../store/AppStore'
import { Card, StatCard } from '../../components/ui'
import { formatMoney, toFa } from '../../utils/format'
import { salesNavItems } from './salesNavigation'
import { salesDocuments, salesSummary } from './salesUtils'
import { FunZoneSalesSyncBanner } from './FunZoneSalesSyncBanner'
import { isFunZoneSalesInvoice } from './funzoneSalesSync'
const processSteps = [
{ step: 1, label: 'پیش‌فاکتور', desc: 'صدور پیشنهاد قیمت برای مشتری', to: '/sales/proforma', icon: '📋' },
{ step: 2, label: 'فاکتور فروش', desc: 'ثبت فروش قطعی و کاهش موجودی', to: '/sales/invoice', icon: '🧾' },
{ step: 3, label: 'دریافت', desc: 'تسویه از مشتری در ماژول دریافت و پرداخت', to: '/treasury', icon: '💰' },
{ step: 4, label: 'فاکتور برگشتی', desc: 'برداشت در انتظار مشتریان و برگشت کالا', to: '/sales/return', icon: '↩️' },
]
export function SalesProcess() {
const { data } = useStore()
const summary = useMemo(() => salesSummary(data), [data])
const funzoneSyncedCount = useMemo(
() => salesDocuments(data.invoices).filter(isFunZoneSalesInvoice).length,
[data.invoices],
)
const quickLinks = salesNavItems.filter((item) => item.to !== '/sales')
return (
<div className="space-y-6">
<FunZoneSalesSyncBanner />
<Card className="p-5">
<h2 className="mb-1 text-lg font-bold text-slate-800">فرایند فروش</h2>
<p className="mb-6 text-sm text-slate-500">
جریان استاندارد فروش مشابه سپیدار: از پیشفاکتور تا فاکتور، دریافت و برگشت.
</p>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
{processSteps.map((step, idx) => (
<div key={step.step} className="relative">
<Link
to={step.to}
className="flex flex-col rounded-xl border border-slate-200 bg-slate-50/50 p-4 transition hover:border-brand-200 hover:bg-brand-50/30"
>
<div className="mb-2 flex items-center gap-2">
<span className="flex h-7 w-7 items-center justify-center rounded-full bg-brand-100 text-xs font-bold text-brand-700">
{toFa(step.step)}
</span>
<span className="text-lg">{step.icon}</span>
</div>
<p className="font-semibold text-slate-800">{step.label}</p>
<p className="mt-1 text-xs text-slate-500">{step.desc}</p>
</Link>
{idx < processSteps.length - 1 && (
<span className="absolute -left-2 top-1/2 hidden -translate-y-1/2 text-slate-300 lg:block"></span>
)}
</div>
))}
</div>
</Card>
<div className="grid grid-cols-2 gap-4 lg:grid-cols-5">
<StatCard label="پیش‌فاکتور" value={toFa(summary.proformaCount)} hint={`${toFa(summary.openProformas)} باز`} />
<StatCard label="فاکتور فروش" value={toFa(summary.invoiceCount)} hint={formatMoney(summary.totalSales)} />
<StatCard label="فاکتور برگشتی" value={toFa(summary.returnCount)} hint={formatMoney(summary.totalReturns)} />
<StatCard label="فروش خالص" value={formatMoney(summary.netSales)} hint={`${toFa(summary.unpaidCount)} تسویه‌نشده`} />
<StatCard label="همگام فان‌زون" value={toFa(funzoneSyncedCount)} hint="فاکتور از پرداخت رویداد" tone="brand" />
</div>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Card className="p-5">
<h3 className="mb-3 font-bold text-slate-800">اتصال به سایر ماژولها</h3>
<ul className="space-y-2 text-sm text-slate-600">
<li className="flex items-start gap-2">
<span>📦</span>
<span>
<strong>انبارداری:</strong> فاکتور فروش قطعی موجودی را کاهش و فاکتور برگشتی آن را افزایش میدهد.
</span>
</li>
<li className="flex items-start gap-2">
<span>📑</span>
<span>
<strong>اسناد حسابداری:</strong> با تأیید فاکتور، سند درآمد/مطالبات بهصورت خودکار صادر میشود.
</span>
</li>
<li className="flex items-start gap-2">
<span>💰</span>
<span>
<strong>دریافت و پرداخت:</strong> دریافت از مشتری مانده حساب را کاهش میدهد.
</span>
</li>
<li className="flex items-start gap-2">
<span></span>
<span>
<strong>اعلامیه بدهکار/بستانکار:</strong> برای اصلاح مانده مشتری بدون فاکتور.
</span>
</li>
</ul>
</Card>
<Card className="p-5">
<h3 className="mb-3 font-bold text-slate-800">دسترسی سریع</h3>
<div className="grid grid-cols-2 gap-2">
{quickLinks.map((item) => (
<Link
key={item.to}
to={item.to}
className="flex items-center gap-2 rounded-lg border border-slate-200 px-3 py-2.5 text-sm font-medium text-slate-700 transition hover:border-brand-200 hover:bg-brand-50/40"
>
<span>{item.icon}</span>
<span>{item.label}</span>
</Link>
))}
</div>
</Card>
</div>
</div>
)
}