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

284
accounting/models.py Normal file
View File

@@ -0,0 +1,284 @@
import uuid
from django.db import models
class Account(models.Model):
"""A node in the chart of accounts (کدینگ حساب‌ها)."""
ACCOUNT_TYPES = [
('asset', 'Asset'),
('liability', 'Liability'),
('equity', 'Equity'),
('income', 'Income'),
('expense', 'Expense'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
code = models.CharField(max_length=20, unique=True, db_index=True)
name = models.CharField(max_length=150)
type = models.CharField(max_length=20, choices=ACCOUNT_TYPES)
parent = models.ForeignKey(
'self', on_delete=models.SET_NULL, null=True, blank=True, related_name='children'
)
is_group = models.BooleanField(default=False)
opening_balance = models.FloatField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'accounting_accounts'
ordering = ['code']
def __str__(self):
return f"{self.code} - {self.name}"
class Party(models.Model):
"""A trading partner (customer or supplier) used by invoices and treasury."""
PARTY_KINDS = [
('customer', 'Customer'),
('supplier', 'Supplier'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
kind = models.CharField(max_length=20, choices=PARTY_KINDS)
name = models.CharField(max_length=150)
phone = models.CharField(max_length=30, blank=True, default='')
economic_code = models.CharField(max_length=50, blank=True, default='')
address = models.TextField(blank=True, default='')
opening_balance = models.FloatField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'accounting_parties'
ordering = ['name']
def __str__(self):
return self.name
class Product(models.Model):
"""An inventory item."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
code = models.CharField(max_length=50, db_index=True)
name = models.CharField(max_length=150)
unit = models.CharField(max_length=30, blank=True, default='')
sale_price = models.FloatField(default=0)
purchase_price = models.FloatField(default=0)
stock = models.FloatField(default=0)
reorder_level = models.FloatField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'accounting_products'
ordering = ['name']
def __str__(self):
return f"{self.code} - {self.name}"
class Voucher(models.Model):
"""A double-entry journal voucher (سند حسابداری)."""
STATUS_CHOICES = [
('draft', 'Draft'),
('posted', 'Posted'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
number = models.PositiveIntegerField(default=0)
date = models.DateField()
description = models.TextField(blank=True, default='')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
source = models.CharField(max_length=120, blank=True, default='', db_index=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'accounting_vouchers'
ordering = ['-date', '-number']
def __str__(self):
return f"Voucher #{self.number}"
class VoucherLine(models.Model):
"""A single debit/credit line within a voucher."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
voucher = models.ForeignKey(Voucher, on_delete=models.CASCADE, related_name='lines')
account = models.ForeignKey(Account, on_delete=models.PROTECT, related_name='voucher_lines')
description = models.CharField(max_length=255, blank=True, default='')
debit = models.FloatField(default=0)
credit = models.FloatField(default=0)
class Meta:
db_table = 'accounting_voucher_lines'
def __str__(self):
return f"Line {self.account_id} D:{self.debit} C:{self.credit}"
class Invoice(models.Model):
"""A sales or purchase invoice (فاکتور)."""
INVOICE_KINDS = [
('sale', 'Sale'),
('purchase', 'Purchase'),
]
STATUS_CHOICES = [
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('paid', 'Paid'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
kind = models.CharField(max_length=20, choices=INVOICE_KINDS)
number = models.PositiveIntegerField(default=0)
party = models.ForeignKey(
Party, on_delete=models.SET_NULL, null=True, blank=True, related_name='invoices'
)
date = models.DateField()
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
note = models.TextField(blank=True, default='')
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'accounting_invoices'
ordering = ['-date', '-number']
def __str__(self):
return f"{self.kind} invoice #{self.number}"
class InvoiceLine(models.Model):
"""A line item within an invoice."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE, related_name='lines')
product = models.ForeignKey(
Product, on_delete=models.PROTECT, null=True, blank=True, related_name='invoice_lines'
)
quantity = models.FloatField(default=0)
unit_price = models.FloatField(default=0)
discount = models.FloatField(default=0)
tax_rate = models.FloatField(default=0)
class Meta:
db_table = 'accounting_invoice_lines'
def __str__(self):
return f"Line {self.product_id} x{self.quantity}"
class TreasuryTxn(models.Model):
"""A treasury receipt or payment (دریافت/پرداخت)."""
KIND_CHOICES = [
('receipt', 'Receipt'),
('payment', 'Payment'),
]
METHOD_CHOICES = [
('cash', 'Cash'),
('bank', 'Bank'),
('cheque', 'Cheque'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
number = models.PositiveIntegerField(default=0)
kind = models.CharField(max_length=20, choices=KIND_CHOICES)
method = models.CharField(max_length=20, choices=METHOD_CHOICES, default='bank')
date = models.DateField()
party = models.ForeignKey(
Party, on_delete=models.SET_NULL, null=True, blank=True, related_name='treasury_txns'
)
amount = models.FloatField(default=0)
reference = models.CharField(max_length=120, blank=True, default='')
description = models.TextField(blank=True, default='')
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'accounting_treasury_txns'
ordering = ['-date', '-number']
def __str__(self):
return f"{self.kind} #{self.number}"
class Employee(models.Model):
"""A payroll employee."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
code = models.CharField(max_length=50, blank=True, default='')
name = models.CharField(max_length=150)
position = models.CharField(max_length=120, blank=True, default='')
base_salary = models.FloatField(default=0)
insurance_no = models.CharField(max_length=50, blank=True, default='')
hire_date = models.DateField(null=True, blank=True)
active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'accounting_employees'
ordering = ['name']
def __str__(self):
return self.name
class Payslip(models.Model):
"""A monthly payslip for an employee (فیش حقوقی)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='payslips')
period = models.CharField(max_length=20, blank=True, default='')
base_salary = models.FloatField(default=0)
overtime = models.FloatField(default=0)
bonus = models.FloatField(default=0)
deductions = models.FloatField(default=0)
insurance = models.FloatField(default=0)
tax = models.FloatField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'accounting_payslips'
ordering = ['-period']
def __str__(self):
return f"Payslip {self.employee_id} - {self.period}"
class CompanySettings(models.Model):
"""Singleton holding company info and the auto-voucher account mapping."""
id = models.PositiveSmallIntegerField(primary_key=True, default=1)
name = models.CharField(max_length=150, blank=True, default='')
economic_code = models.CharField(max_length=50, blank=True, default='')
fiscal_year = models.CharField(max_length=20, blank=True, default='')
base_currency = models.CharField(max_length=20, blank=True, default='تومان')
tax_rate = models.FloatField(default=0)
address = models.TextField(blank=True, default='')
phone = models.CharField(max_length=40, blank=True, default='')
bank_account = models.ForeignKey(
Account, on_delete=models.SET_NULL, null=True, blank=True, related_name='+'
)
owner_payable_account = models.ForeignKey(
Account, on_delete=models.SET_NULL, null=True, blank=True, related_name='+'
)
customer_payable_account = models.ForeignKey(
Account, on_delete=models.SET_NULL, null=True, blank=True, related_name='+'
)
owner_share_account = models.ForeignKey(
Account, on_delete=models.SET_NULL, null=True, blank=True, related_name='+'
)
sales_income_account = models.ForeignKey(
Account, on_delete=models.SET_NULL, null=True, blank=True, related_name='+'
)
class Meta:
db_table = 'accounting_company_settings'
def __str__(self):
return self.name or 'Company Settings'