Initial commit: standalone Django accounting API with admin JWT authorization

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Shayan Azadi
2026-07-01 00:29:25 +03:30
commit 066276743a
21 changed files with 1279 additions and 0 deletions

103
accounting/seeding.py Normal file
View File

@@ -0,0 +1,103 @@
"""Default chart of accounts and company settings (zeroed opening balances).
Shared by the initial data migration and the runtime "reset" endpoint so both
produce an identical clean starting point with no demo business data.
"""
# (code, name, type, is_group, parent_code)
DEFAULT_ACCOUNTS = [
('10', 'دارایی‌های جاری', 'asset', True, None),
('1001', 'صندوق', 'asset', False, '10'),
('1002', 'بانک ملت', 'asset', False, '10'),
('1003', 'بانک سامان', 'asset', False, '10'),
('1004', 'حساب‌های دریافتنی (مشتریان)', 'asset', False, '10'),
('1005', 'موجودی کالا', 'asset', False, '10'),
('1006', 'پیش‌پرداخت‌ها', 'asset', False, '10'),
('15', 'دارایی‌های ثابت', 'asset', True, None),
('1501', 'اموال و تجهیزات', 'asset', False, '15'),
('1502', 'استهلاک انباشته', 'asset', False, '15'),
('20', 'بدهی‌های جاری', 'liability', True, None),
('2001', 'حساب‌های پرداختنی (تأمین‌کنندگان)', 'liability', False, '20'),
('2002', 'مالیات بر ارزش افزوده پرداختنی', 'liability', False, '20'),
('2003', 'حقوق پرداختنی', 'liability', False, '20'),
('2004', 'بیمه پرداختنی', 'liability', False, '20'),
('2005', 'بدهی به مالکان (کیف پول)', 'liability', False, '20'),
('2006', 'بدهی به مشتریان (کیف پول)', 'liability', False, '20'),
('30', 'حقوق صاحبان سهام', 'equity', True, None),
('3001', 'سرمایه', 'equity', False, '30'),
('3002', 'سود انباشته', 'equity', False, '30'),
('40', 'درآمدها', 'income', True, None),
('4001', 'درآمد فروش', 'income', False, '40'),
('4002', 'درآمد خدمات', 'income', False, '40'),
('50', 'هزینه‌ها', 'expense', True, None),
('5001', 'بهای تمام‌شده کالای فروش‌رفته', 'expense', False, '50'),
('5002', 'هزینه حقوق و دستمزد', 'expense', False, '50'),
('5003', 'هزینه اجاره', 'expense', False, '50'),
('5004', 'هزینه آب، برق و گاز', 'expense', False, '50'),
('5005', 'هزینه تبلیغات', 'expense', False, '50'),
('5006', 'هزینه استهلاک', 'expense', False, '50'),
('5007', 'سهم مالکان از فروش', 'expense', False, '50'),
]
DEFAULT_SETTINGS = {
'name': 'مجموعه تفریحی فان‌زون',
'economic_code': '',
'fiscal_year': '1404',
'base_currency': 'تومان',
'tax_rate': 10,
'address': '',
'phone': '',
}
# Mapping of CompanySettings FK attribute -> account code.
SETTINGS_ACCOUNT_MAP = {
'bank_account_id': '1002',
'owner_payable_account_id': '2005',
'customer_payable_account_id': '2006',
'owner_share_account_id': '5007',
'sales_income_account_id': '4001',
}
def create_default_accounts(Account):
"""Creates the standard chart of accounts with zeroed balances.
Returns a dict mapping account code -> created Account instance.
"""
by_code = {}
# First pass: create all accounts without parents.
for code, name, type_, is_group, _parent in DEFAULT_ACCOUNTS:
by_code[code] = Account.objects.create(
code=code, name=name, type=type_, is_group=is_group, opening_balance=0,
)
# Second pass: wire up parents.
for code, _name, _type, _is_group, parent_code in DEFAULT_ACCOUNTS:
if parent_code:
account = by_code[code]
account.parent = by_code[parent_code]
account.save(update_fields=['parent'])
return by_code
def ensure_company_settings(CompanySettings, accounts_by_code):
"""Creates (or resets) the singleton settings with the default mapping."""
defaults = dict(DEFAULT_SETTINGS)
for attr, code in SETTINGS_ACCOUNT_MAP.items():
account = accounts_by_code.get(code)
defaults[attr] = account.id if account else None
CompanySettings.objects.update_or_create(id=1, defaults=defaults)
def seed_default_data(Account, CompanySettings):
"""Seeds chart of accounts + settings if no accounts exist yet."""
if Account.objects.exists():
accounts_by_code = {a.code: a for a in Account.objects.all()}
else:
accounts_by_code = create_default_accounts(Account)
ensure_company_settings(CompanySettings, accounts_by_code)
return accounts_by_code