Skip to content
arynullPublic

About

Persian invoice generator CLI — Jalali dates, RTL invoices, PDF export

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

15 Commits

Folders and files

Repository files navigation

فاکتور (factor)

ابزار خط فرمان برای صدور و مدیریت فاکتور فارسی با تاریخ شمسی، مالیات و تخفیف.

معرفی

فاکتور یک برنامه خط فرمان (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.0

ساخت فاکتور (new)

usage: 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"
2

فهرست فاکتورها (list)

usage: 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.

نمایش فاکتور (show)

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) خطای تمیز می‌دهد و کد خروج غیرصفر برمی‌گرداند.

کپی فاکتور (duplicate)

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 2
Invoice #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) خطای تمیز می‌دهد و کد خروج غیرصفر برمی‌گرداند.

پرداخت و ابطال (pay / void)

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 1
Invoice #1 marked as paid.
factor show 1
Invoice #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.00
factor void 1
Invoice #1 voided.

شماره فاکتور باید عدد صحیح مثبت باشد؛ شماره ناموجود یا نامعتبر (مثل 0 یا -5) خطای تمیز می‌دهد و کد خروج غیرصفر برمی‌گرداند.

خلاصه درآمد (stats)

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.00

مشتریان (customer)

usage: factor customer [-h] {add,list} ...

افزودن مشتری (customer add)

usage: factor customer add [-h] --name NAME [--phone PHONE]
                           [--address ADDRESS]

مثال:

factor customer add --name Acme --phone 09120000000 --address "تهران"

خروجی (شناسه مشتری جدید):

1

نام مشتری یکتا و اجباری است: نام تکراری یا نام خالی/فقط فاصله خطای تمیز می‌دهد و کد خروج غیرصفر برمی‌گرداند.

اگر هنگام ساخت فاکتور (new --customer NAME) مشتری‌ای با دقیقاً همان نام وجود داشته باشد، فاکتور به آن مشتری متصل می‌شود؛ در غیر این صورت نام به صورت متن ذخیره می‌شود و اتصالی برقرار نمی‌شود.

فهرست مشتریان (customer list)

usage: factor customer list [-h]

مثال:

factor customer list

نمونه خروجی:

  ID  Name                  Phone            Address
   1  Acme                  09120000000      تهران

اگر مشتری‌ای نباشد:

No customers.

خروجی HTML و PDF (render)

usage: factor render [-h] [-o PATH] [--pdf] number

خروجی HTML

بدون -o، سند HTML در خروجی استاندارد (stdout) چاپ می‌شود؛ با -o در فایل ذخیره می‌شود:

factor render 1 -o invoice.html

سند HTML مستقل است: <html lang="fa" dir="rtl">، متای UTF-8، قلم‌های سیستمی (Vazirmatn، Tahoma) بدون نیاز به اینترنت، استایل چاپ (@media print مناسب A4)، برچسب‌های فارسی، اعداد فارسی با جداکننده هزارگان، تاریخ شمسی و جدول اقلام با جمع اقلام، تخفیف، مالیات و مبلغ نهایی. متن‌های کاربر (نام مشتری و شرح اقلام) escape می‌شوند تا размет نشکنند.

خروجی PDF

factor render 1 --pdf -o invoice.pdf

پرچم --pdf بدون -o خطا می‌دهد (نوشتن PDF باینری در stdout پشتیبانی نمی‌شود). خروجی یک فایل PDF معتبر است (با %PDF- شروع می‌شود) که شامل شماره فاکتور، نام مشتری، تاریخ شمسی، اقلام و جمع‌ها با متن فارسی خوانا (شکل‌دهی و ترتیب حروف با arabic-reshaper و python-bidi، قلم DejaVuSans) و بلوک مشخصات راست‌به‌چپ (برچسب سمت راست، مقدار کنار آن) است.

خروجی CSV (export)

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 export
number,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 1
Subtotal: 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_DATA_DIR

پیش‌فرض پایگاه داده این مسیر است:

~/.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 کد خروج ۲ دارند.

About

Persian invoice generator CLI — Jalali dates, RTL invoices, PDF export

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages