diff --git a/src/assets/images/sign.png b/src/assets/images/sign.png new file mode 100644 index 0000000..ebff7f3 Binary files /dev/null and b/src/assets/images/sign.png differ diff --git a/src/components/Invoice.css b/src/components/Invoice.css new file mode 100644 index 0000000..c37c8ef --- /dev/null +++ b/src/components/Invoice.css @@ -0,0 +1,146 @@ +.invoice-container { + padding: 0; + margin: 0 auto; + max-width: 29.7cm; + -webkit-print-color-adjust: exact; + direction: rtl; + font: 9pt irancell; +} + +.page { + background: white; + page-break-after: always; + padding: 0.5cm; +} + +.flex { + display: flex; +} + +.flex > * { + float: left; +} + +.flex-grow { + flex-grow: 10000000; +} + +.barcode { + text-align: center; + margin: 12px 0 0 0; + height: 30px; +} + +.barcode span { + font-size: 35pt; + font-family: "Libre Barcode 128"; +} + +.portait { + transform: rotate(-90deg) translate(0, 40%); + text-align: center; +} + +.header-item-wrapper { + border: 1px solid #000; + width: 100%; + height: 100%; + background: #eee; + display: flex; + align-content: center; +} + +thead, +tfoot { + background: #eee; +} + +.header-item-data { + height: 100%; + width: 100%; +} + +.bordered { + border: 1px solid #000; + padding: 0.12cm; +} + +.header-table { + width: 100%; + table-layout: fixed; + border-spacing: 0; +} + +.header-table td { + padding: 0; + vertical-align: top; +} + +.content-table { + border-collapse: collapse; + width: 100%; + table-layout: fixed; + border-spacing: 0; +} + +.content-table td, +.content-table th { + border: 1px solid #000; + text-align: center; + padding: 0.1cm; + font-weight: normal; +} + +.centered td { + vertical-align: middle; +} + +.serials { + direction: ltr; + text-align: left; +} + +.title { + text-align: right; +} + +.grow { + width: 100%; + height: 100%; +} + +.font-small { + font-size: 8pt; +} + +.font-medium { + font-size: 10pt; +} + +.font-big { + font-size: 15pt; +} + +.label { + font-weight: bold; + padding: 0 0 0 2px; +} + +.ltr { + direction: ltr; + display: block; +} + +@media print { + .print-button { + display: none; + visibility: hidden; + } +} + +@page { + size: A4 landscape; + margin: 0; + margin-bottom: 0.5cm; + margin-top: 0.5cm; +} diff --git a/src/components/Invoice.tsx b/src/components/Invoice.tsx new file mode 100644 index 0000000..29e53b7 --- /dev/null +++ b/src/components/Invoice.tsx @@ -0,0 +1,278 @@ +import { useEffect } from 'react'; +import './Invoice.css'; +import { useParams } from 'react-router-dom'; +import { useGetInvoiceDetail } from '../pages/receipts/hooks/useReceiptData'; +import { useGetProfile } from '../pages/profile/hooks/useProfileData'; +import moment from 'moment-jalaali'; +import { NumberFormat } from '../config/func'; +import { ReceiptDetailItemType } from '../pages/receipts/types/ReceiptTypes'; +import SignImage from '../assets/images/sign.png' +import { useTranslation } from 'react-i18next'; +import Button from './Button'; +import { Printer } from 'iconsax-react'; + +const Invoice = () => { + const { t } = useTranslation('global') + const { id } = useParams(); + const getInvoice = useGetInvoiceDetail(id ? id : ''); + const getProfile = useGetProfile(); + + useEffect(() => { + const handleResize = () => { + try { + const mvp = document.getElementById('vp'); + if (!mvp) return; + + if (screen.width >= 300 && screen.width < 500) { + mvp.setAttribute('content', 'initial-scale=0.35,width=device-width'); + } else if (screen.width > 500 && screen.width < 600) { + mvp.setAttribute('content', 'initial-scale=0.6,width=device-width'); + } else if (screen.width > 600 && screen.width < 700) { + mvp.setAttribute('content', 'initial-scale=0.7,width=device-width'); + } + } catch (e) { + console.error('Error setting viewport:', e); + } + }; + + handleResize(); + window.addEventListener('resize', handleResize); + + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + const handlePrint = () => { + // Add print-specific styles before printing + const style = document.createElement('style'); + style.innerHTML = ` + @media print { + @page { + margin: 0; + } + body * { + visibility: hidden; + } + .invoice-container, .invoice-container * { + visibility: visible; + } + } + `; + document.head.appendChild(style); + window.print(); + document.head.removeChild(style); + }; + + if (getInvoice.isPending || getProfile.isPending) { + return
; + } + + const invoice = getInvoice.data?.data?.invoice; + const profile = getProfile.data?.data?.user; + + return ( +
+ + + +
+

+ {invoice?.status === 'PAID' ? 'فاکتور (پرداخت شده)' : 'پیش فاکتور (پرداخت نشده)'} +

+ + + + + + + + + + + +
+
+
فروشندده
+
+
+
+ + + + + + + + + + + + +
+ فروشنده: رسا نگاره داناک + + شناسه ملی: ۱۴۰۰۵۴۵۳۱۹۰ + + شماره ثبت: ۴۸۳۵۱۶ + + شماره اقتصادی: ۱۴۰۰۵۴۵۳۱۹۰ +
+ نشانی شرکت:تهران - یوسف آباد - خیابان فتحی شقاقی - پلاک ۵ - واحد ۱۳ + + کدپستی: ۱۴۳۳۶۳۴۴۴۶ + + تلفن و فکس: ۰۲۱۹۱۶۹۳۵۳۳ +
+
+
+
+
+
شماره فاکتور:
+
{moment(invoice?.createdAt).format('jYYYY') + invoice?.numericId}
+
+
+
تاریخ:
+
{moment(invoice?.createdAt).format('jYYYY/jMM/jDD')}
+
+
+
پیگیری:
+
{invoice?.numericId}
+
+
+
+
+
خریدار
+
+
+
+ + + + + + + + + + + + +
+ خریدار: {profile?.financialType === 'LEGAL' ? profile?.legalUser?.registrationName : `${profile?.firstName} ${profile?.lastName}`} + + شناسه ملی: {profile?.nationalCode || '-'} + + شماره ثبت: {profile?.financialType === 'LEGAL' ? profile?.legalUser?.registrationCode : '-'} + + شماره اقتصادی: {profile?.financialType === 'LEGAL' ? profile?.legalUser?.economicCode : '-'} +
+ نشانی: {profile?.financialType === 'LEGAL' ? profile?.legalUser?.address?.fullAddress : profile?.address?.fullAddress || '-'} + + کدپستی: {profile?.financialType === 'LEGAL' ? profile?.legalUser?.address?.postalCode : profile?.address?.postalCode || '-'} + + تلفن: {profile?.phone || '-'} +
+
+
+
+
+
نوع شخص:
+
{profile?.financialType === 'LEGAL' ? 'حقوقی' : 'حقیقی'}
+
+
+
وضعیت:
+
{t(`receip.${invoice?.status}`)}
+
+
+
تاریخ پرداخت:
+
{invoice?.paidAt ? moment(invoice?.paidAt).format('jYYYY/jMM/jDD') : '-'}
+
+
+
+ + + + + + + + + + + + + + + + + + {invoice?.items?.map((item: ReceiptDetailItemType, index: number) => ( + + + + + + + + + + + + + + ))} + + + + + + + + + + + + + + + +
ردیفشناسه کالا یا خدمتشرح کالا یا خدمتآمرتعدادمبلغ واحد (ریال)مبلغ کل (ریال)تخفیف (ریال)مبلغ کل پس از تخفیف (ریال)جمع مالیات و عوارض ارزش افزوده (ریال)جمع کل پس از تخفیف و مالیات و عوارض (ریال)
{index + 1}{item.id.slice(-5)}{item.name}داناک{item.count}{NumberFormat(Number(item.unitPrice))}{NumberFormat(item.totalPrice)}{NumberFormat(Number(item.discount))}{NumberFormat(item.totalPrice - Number(item.discount))}{NumberFormat(invoice.tax)}{NumberFormat(invoice.totalPrice)}
+ جمع کل پس از کسر تخفیف با احتساب مالیات و عوارض (ریال): + {NumberFormat(invoice?.totalPrice)}
+ اعتبار مالیاتی قابل استفاده توسط خریدار + +
+
+
+
مهر و امضای فروشنده:
+
تاریخ خرید:
+
+
+
+ +
+
+ {moment(invoice?.paidAt).format('jYYYY/jMM/jDD')} +
+
+
+
+
+
+ ); +}; + +export default Invoice; \ No newline at end of file diff --git a/src/components/PDFGenerator.tsx b/src/components/PDFGenerator.tsx index 5b0302c..63bb13e 100644 --- a/src/components/PDFGenerator.tsx +++ b/src/components/PDFGenerator.tsx @@ -1,65 +1,27 @@ -import { jsPDF } from 'jspdf'; -import html2canvas from 'html2canvas'; import { FC } from 'react'; import Button from './Button'; import { useTranslation } from 'react-i18next'; +import { useGetInvoiceDetail } from '../pages/receipts/hooks/useReceiptData'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useGetProfile } from '../pages/profile/hooks/useProfileData'; +import { Pages } from '../config/Pages'; -interface PDFGeneratorProps { - elementId: string; - fileName: string; -} -const PDFGenerator: FC = ({ elementId, fileName }) => { +const PDFGenerator: FC = () => { + const { id } = useParams() const { t } = useTranslation('global'); - - const generatePDF = async () => { - const element = document.getElementById(elementId); - if (!element) return; - - // تنظیم استایل‌های موقت برای PDF - const originalStyles = element.getAttribute('style') || ''; - element.setAttribute('style', ` - ${originalStyles} - width: 800px !important; - margin: 0 auto !important; - padding: 20px !important; - `); - - const canvas = await html2canvas(element, { - scale: 1.5, // کاهش کیفیت برای سایز کمتر - useCORS: true, - logging: false, - backgroundColor: '#ffffff', - windowWidth: 800, - width: 800 - }); - - // برگرداندن استایل‌های اصلی - element.setAttribute('style', originalStyles); - - const imgData = canvas.toDataURL('image/jpeg', 0.8); // استفاده از JPEG با کیفیت 80% - const pdf = new jsPDF({ - orientation: 'portrait', - unit: 'mm', - format: 'a4' - }); - - const imgWidth = 180; // کاهش عرض برای حاشیه بیشتر - const imgHeight = (canvas.height * imgWidth) / canvas.width; - - // محاسبه موقعیت برای قرار دادن در وسط صفحه - const x = (210 - imgWidth) / 2; // 210 عرض A4 در میلی‌متر - const y = (297 - imgHeight) / 2; // 297 ارتفاع A4 در میلی‌متر - - pdf.addImage(imgData, 'JPEG', x, y, imgWidth, imgHeight); - pdf.save(`${fileName}.pdf`); - }; + const navigate = useNavigate() + const getInvoce = useGetInvoiceDetail(id ? id : '') + const getProfile = useGetProfile() + const status = getInvoce.data?.data?.invoice?.status + const financialType = getProfile.data?.data?.user?.financialType return (