Initial commit: standalone Django accounting API with admin JWT authorization
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
284
accounting/models.py
Normal file
284
accounting/models.py
Normal 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'
|
||||
Reference in New Issue
Block a user