ابزار خط فرمان برای صدور و مدیریت فاکتور فارسی با تاریخ شمسی، مالیات و تخفیف.
فاکتور یک برنامه خط فرمان (CLI) است که فاکتورها را در یک پایگاه داده SQLite ذخیره میکند و میتواند آنها را به صورت HTML راستبهچپ فارسی یا PDF نمایش دهد.
امکانات:
- ساخت فاکتور با چند قلم کالا (
new) - فهرست و نمایش جزئیات فاکتورها (
listوshow) - کپی فاکتور موجود در یک فاکتور جدید (
duplicate) - خلاصه درآمد کل فاکتورها در یک نگاه (
stats) - دفترچه مشتریان (
customer addوcustomer list) با اتصال خودکار نام مشتری به فاکتور - مالیات درصدی و تخفیف ثابت با محاسبات دقیق اعشاری (Decimal)
- نمایش تاریخ به شمسی (ذخیرهسازی به میلادی UTC، تبدیل فقط نمایشی است)
- خروجی HTML فارسی راستبهچپ و PDF
- اعتبارسنجی کامل ورودیها: هر خطایی با پیام فارسی/انگلیسی تمیز در stderr و کد خروج غیرصفر گزارش میشود، بدون traceback
پیشنیاز: پایتون ۳.۱۲ یا جدیدتر.
git clone https://github.com/arynull/factor.git factor
cd factor
python3.12 -m venv .venv
.venv/bin/pip install .یا برای توسعه (نصب قابل ویرایش):
.venv/bin/pip install -e .وابستگیها (jdatetime، fpdf2، arabic-reshaper، python-bidi) همراه نصب میآیند.
بررسی نصب:
factor --versionخروجی:
1.3.0usage: factor new [-h] --customer CUSTOMER --item DESC:QTY:UNIT_PRICE
[--tax PCT] [--discount AMOUNT]
هر قلم کالا با قالب شرح:تعداد:مبلغ واحد داده میشود و با تکرار --item میتوان چند قلم اضافه کرد. تعداد باید عدد مثبت باشد و مبلغ واحد عدد نامنفی.
مثال:
factor new --customer Acme --item "Widget:2:10.00" --item "Gadget:1:30.00"خروجی (شماره فاکتور جدید):
1فاکتور دوم شماره بعدی را میگیرد:
factor new --customer Beta --item "Pin:5:1.00"2usage: factor list [-h]
مثال:
factor listنمونه خروجی (تاریخها شمسی هستند):
No. Customer Date Total
1 Acme 1405-07-09 50.00
2 Beta 1405-07-09 5.00اگر فاکتوری نباشد:
No invoices.usage: factor show [-h] number
مثال:
factor show 1نمونه خروجی:
Invoice #1
Customer: Acme
Date: 1405-07-09
Items:
Widget x 2 @ 10.00 = 20.00
Gadget x 1 @ 30.00 = 30.00
Subtotal: 50.00
Discount: 0.00
Tax (0.00%): 0.00
Total: 50.00شماره فاکتور باید عدد صحیح مثبت باشد؛ شماره ناموجود یا نامعتبر (مثل 0 یا -5) خطای تمیز میدهد و کد خروج غیرصفر برمیگرداند.
usage: factor duplicate [-h] number
از یک فاکتور موجود یک کپی جدید میسازد: نام مشتری، درصد مالیات، تخفیف ثابت و همه اقلام (شرح، تعداد، مبلغ واحد) عیناً کپی میشوند. شماره فاکتور جدید به صورت خودکار (بیشترین شماره موجود + ۱) و تاریخ ثبت تازه اختصاص مییابد.
مثال:
factor new --customer Acme --item "Widget:2:10.00" --tax 9 --discount 5.00
factor duplicate 1خروجی (شماره فاکتور جدید):
2فاکتور جدید همان جمعها را دارد:
factor show 2Invoice #2
Customer: Acme
Date: 1405-07-09
Items:
Widget x 2 @ 10.00 = 20.00
Subtotal: 20.00
Discount: 5.00
Tax (9.00%): 1.35
Total: 16.35شماره فاکتور باید عدد صحیح مثبت باشد؛ شماره ناموجود یا نامعتبر (مثل 0 یا -5) خطای تمیز میدهد و کد خروج غیرصفر برمیگرداند.
usage: factor pay [-h] number
usage: factor void [-h] number
هر فاکتور تازه با وضعیت issued ساخته میشود. pay فاکتور issued را به paid میبرد و void فاکتور issued یا paid را باطل میکند. پرداخت دوباره، پرداخت فاکتور باطلشده و ابطال دوباره خطای تمیز میدهند و کد خروج غیرصفر برمیگردانند.
مثال:
factor new --customer Acme --item "Widget:2:10.00"
factor pay 1Invoice #1 marked as paid.factor show 1Invoice #1
Customer: Acme
Date: 1405-07-09
Status: paid
Items:
Widget x 2 @ 10.00 = 20.00
Subtotal: 20.00
Discount: 0.00
Tax (0.00%): 0.00
Total: 20.00factor void 1Invoice #1 voided.شماره فاکتور باید عدد صحیح مثبت باشد؛ شماره ناموجود یا نامعتبر (مثل 0 یا -5) خطای تمیز میدهد و کد خروج غیرصفر برمیگرداند.
usage: factor stats [-h]
جمع همه فاکتورها را در شش خط نشان میدهد: تعداد فاکتورها، جمع اقلام، جمع تخفیفها، جمع مالیاتها، جمع مبلغهای نهایی و میانگین هر فاکتور. همه محاسبات اعشاری دقیق (Decimal) و میانگین با گرد شدن نیمبهبالا تا ۲ رقم اعشار محاسبه میشود. همه فاکتورها بدون توجه به وضعیت (issued/paid/void) حساب میشوند.
مثال — سه فاکتور بسازیم:
factor new --customer "مشتری نمونه" --item "خدمات طراحی:1:120.00" --item "پشتیبانی ماهانه:2:15.00"
factor new --customer "شرکت پارس" --item "Widget:2:10.00" --tax 9 --discount 5
factor new --customer "آژانس نور" --item "Gadget:1:30.00"factor statsخروجی:
Invoices: 3
Subtotal: 200.00
Discount: 5.00
Tax: 1.35
Total: 196.35
Average: 65.45اگر هیچ فاکتوری ثبت نشده باشد، دستور خطا نیست؛ همه خطوط مبلغ صفر و کد خروج صفر است:
Invoices: 0
Subtotal: 0.00
Discount: 0.00
Tax: 0.00
Total: 0.00
Average: 0.00usage: factor customer [-h] {add,list} ...
usage: factor customer add [-h] --name NAME [--phone PHONE]
[--address ADDRESS]
مثال:
factor customer add --name Acme --phone 09120000000 --address "تهران"خروجی (شناسه مشتری جدید):
1نام مشتری یکتا و اجباری است: نام تکراری یا نام خالی/فقط فاصله خطای تمیز میدهد و کد خروج غیرصفر برمیگرداند.
اگر هنگام ساخت فاکتور (new --customer NAME) مشتریای با دقیقاً همان نام وجود داشته باشد، فاکتور به آن مشتری متصل میشود؛ در غیر این صورت نام به صورت متن ذخیره میشود و اتصالی برقرار نمیشود.
usage: factor customer list [-h]
مثال:
factor customer listنمونه خروجی:
ID Name Phone Address
1 Acme 09120000000 تهراناگر مشتریای نباشد:
No customers.usage: factor render [-h] [-o PATH] [--pdf] number
بدون -o، سند HTML در خروجی استاندارد (stdout) چاپ میشود؛ با -o در فایل ذخیره میشود:
factor render 1 -o invoice.htmlسند HTML مستقل است: <html lang="fa" dir="rtl">، متای UTF-8، قلمهای سیستمی (Vazirmatn، Tahoma) بدون نیاز به اینترنت، استایل چاپ (@media print مناسب A4)، برچسبهای فارسی، اعداد فارسی با جداکننده هزارگان، تاریخ شمسی و جدول اقلام با جمع اقلام، تخفیف، مالیات و مبلغ نهایی. متنهای کاربر (نام مشتری و شرح اقلام) escape میشوند تا размет نشکنند.
factor render 1 --pdf -o invoice.pdfپرچم --pdf بدون -o خطا میدهد (نوشتن PDF باینری در stdout پشتیبانی نمیشود). خروجی یک فایل PDF معتبر است (با %PDF- شروع میشود) که شامل شماره فاکتور، نام مشتری، تاریخ شمسی، اقلام و جمعها با متن فارسی خوانا (شکلدهی و ترتیب حروف با arabic-reshaper و python-bidi، قلم DejaVuSans) و بلوک مشخصات راستبهچپ (برچسب سمت راست، مقدار کنار آن) است.
usage: factor export [-h] [-o PATH]
همه فاکتورها را در قالب CSV ماشینی برای اکسل و اسکریپتها خروجی میدهد. ستونها به ترتیب number,customer,date,subtotal,discount,tax,total هستند. تاریخ شمسی (مثل 1405-07-09) و مبلغها با دو رقم اعشار و ارقام انگلیسی نوشته میشوند تا ماشینخوان بمانند (ارقام فارسی استفاده نمیشود). سطر سرستون همیشه چاپ میشود؛ اگر فاکتوری نباشد فقط سرستون برمیگردد و کد خروج صفر است. همه فاکتورها بدون توجه به وضعیت (issued/paid/void) صادر میشوند.
بدون -o در stdout با UTF-8 بدون BOM:
factor new --customer Acme --item "Widget:2:10.00"
factor exportnumber,customer,date,subtotal,discount,tax,total
1,Acme,1405-07-09,20.00,0.00,0.00,20.00با -o در فایل با BOM (utf-8-sig) تا اکسل متن فارسی را درست باز کند:
factor export -o invoices.csvنام مشتری با کاما یا گیومه بهدرستی با استاندارد csv نقلقول میشود و با csv.reader برمیگردد.
--tax PCT tax percent, decimal >= 0 (e.g. --tax 9)
--discount AMOUNT fixed discount amount, decimal >= 0, must not exceed
subtotal
هر دو اختیاری و پیشفرض صفر هستند. مالیات درصد (--tax 9 یعنی ۹٪) و تخفیف مبلغ ثابت است و نباید از جمع اقلام بیشتر باشد.
فرمول (اعشاری دقیق، گرد شدن نیمبهبالا در هر مرحله تا ۲ رقم اعشار):
- مبلغ مشمول = جمع اقلام − تخفیف
- مبلغ مالیات = مبلغ مشمول × درصد مالیات ÷ ۱۰۰
- مبلغ نهایی = مبلغ مشمول + مبلغ مالیات
مثال:
factor new --customer Acme --item "Widget:2:10.00" --tax 9 --discount 5.00جمع اقلام ۲۰ است؛ مشمول ۱۵؛ مالیات ۱٫۳۵؛ نهایی ۱۶٫۳۵:
factor show 1Subtotal: 20.00
Discount: 5.00
Tax (9.00%): 1.35
Total: 16.35مالیات منفی، تخفیف منفی و تخفیف بیشتر از جمع اقلام خطای تمیز میدهند و چیزی در پایگاه داده نوشته نمیشود.
تاریخها به صورت UTC میلادی (ISO-8601) ذخیره میشوند و فقط هنگام نمایش به شمسی تبدیل میشوند. مثلاً factor list و factor show تاریخ را به شکل 1405-07-09 نشان میدهند. در خروجیهای HTML و PDF هم تاریخ شمسی با ارقام فارسی نمایش داده میشود.
پیشفرض پایگاه داده این مسیر است:
~/.factor/factor.dbبا متغیر محیطی FACTOR_DATA_DIR میتوان پوشه داده را عوض کرد:
FACTOR_DATA_DIR=/tmp/my-data factor listاگر فایل پایگاه داده خراب/ناخوانا باشد یا پوشه داده قابل نوشتن نباشد، هر دستوری با پیام خطای تمیز (Error: ...) در stderr و کد خروج غیرصفر تمام میشود، بدون traceback.
new: شماره فاکتور جدید در stdout (مثلاً1).duplicate: شماره فاکتور جدید در stdout (مثلاً2).customer add: شناسه مشتری جدید در stdout (مثلاً1).listوcustomer list: جدول متنی؛ اگر خالی باشند به ترتیبNo invoices.وNo customers..show: جزئیات متنی فاکتور (سربرگ، اقلام، جمع اقلام، تخفیف، مالیات، مبلغ نهایی).stats: شش خط خلاصه درآمد (تعداد فاکتور، جمع اقلام، جمع تخفیف، جمع مالیات، جمع نهایی، میانگین)؛ برای پایگاه داده خالی همه خطوط صفر و کد خروج صفر است.renderبدون-o: سند HTML در stdout. با-o: نوشتن HTML در فایل. با--pdf -o: نوشتن PDF در فایل.export: خروجی CSV با سرستونnumber,customer,date,subtotal,discount,tax,total؛ بدون-oدر stdout (UTF-8)، با-oدر فایل با BOM (utf-8-sig)؛ برای پایگاه داده خالی فقط سرستون.- خطاها: پیام
Error: ...در stderr با کد خروج غیرصفر؛ هیچوقت traceback چاپ نمیشود. خطاهای نحوی argparse کد خروج ۲ دارند.