Added type hints to the accounting application.
This commit is contained in:
parent
9cb6f25ee5
commit
d4e7458117
@ -142,14 +142,17 @@ class CashAccountConverter:
|
|||||||
"""The path converter for the cash account."""
|
"""The path converter for the cash account."""
|
||||||
regex = "0|(11|12|21|22)[1-9]{1,3}"
|
regex = "0|(11|12|21|22)[1-9]{1,3}"
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: str) -> Account:
|
||||||
"""Returns the cash account by the account code.
|
"""Returns the cash account by the account code.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (str): The account code.
|
value: The account code.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Account: The account.
|
The account.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: When the value is invalid
|
||||||
"""
|
"""
|
||||||
if value == "0":
|
if value == "0":
|
||||||
return Account(
|
return Account(
|
||||||
@ -164,14 +167,14 @@ class CashAccountConverter:
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
return account
|
return account
|
||||||
|
|
||||||
def to_url(self, value):
|
def to_url(self, value: Account) -> str:
|
||||||
"""Returns the code of an account.
|
"""Returns the code of an account.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (Account): The account.
|
value: The account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The account code.
|
The account code.
|
||||||
"""
|
"""
|
||||||
return value.code
|
return value.code
|
||||||
|
|
||||||
@ -180,14 +183,17 @@ class LedgerAccountConverter:
|
|||||||
"""The path converter for the ledger account."""
|
"""The path converter for the ledger account."""
|
||||||
regex = "[1-9]{1,5}"
|
regex = "[1-9]{1,5}"
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: str) -> Account:
|
||||||
"""Returns the ledger account by the account code.
|
"""Returns the ledger account by the account code.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (str): The account code.
|
value: The account code.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Account: The account.
|
The account.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: When the value is invalid
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
account = Account.objects.get(code=value)
|
account = Account.objects.get(code=value)
|
||||||
@ -197,14 +203,14 @@ class LedgerAccountConverter:
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
return account
|
return account
|
||||||
|
|
||||||
def to_url(self, value):
|
def to_url(self, value: Account) -> str:
|
||||||
"""Returns the code of an account.
|
"""Returns the code of an account.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (Account): The account.
|
value: The account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The account code.
|
The account code.
|
||||||
"""
|
"""
|
||||||
return value.code
|
return value.code
|
||||||
|
|
||||||
@ -213,27 +219,30 @@ class TransactionConverter:
|
|||||||
"""The path converter for the accounting transactions."""
|
"""The path converter for the accounting transactions."""
|
||||||
regex = "[1-9][0-9]{8}"
|
regex = "[1-9][0-9]{8}"
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: str) -> Transaction:
|
||||||
"""Returns the transaction by the transaction ID.
|
"""Returns the transaction by the transaction ID.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (str): The transaction ID.
|
value: The transaction ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Transaction: The account.
|
The account.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: When the value is invalid
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return Transaction.objects.get(pk=value)
|
return Transaction.objects.get(pk=value)
|
||||||
except Transaction.DoesNotExist:
|
except Transaction.DoesNotExist:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def to_url(self, value):
|
def to_url(self, value: Transaction) -> str:
|
||||||
"""Returns the ID of an account.
|
"""Returns the ID of an account.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (Transaction): The transaction.
|
value: The transaction.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The transaction ID.
|
The transaction ID.
|
||||||
"""
|
"""
|
||||||
return value.pk
|
return value.pk
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
@ -67,11 +68,11 @@ class RecordForm(forms.Form):
|
|||||||
self.txn_form = None
|
self.txn_form = None
|
||||||
self.is_credit = None
|
self.is_credit = None
|
||||||
|
|
||||||
def account_title(self):
|
def account_title(self) -> Optional[str]:
|
||||||
"""Returns the title of the specified account, if any.
|
"""Returns the title of the specified account, if any.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The title of the specified account, or None if the specified
|
The title of the specified account, or None if the specified
|
||||||
account is not available.
|
account is not available.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
@ -101,7 +102,7 @@ class RecordForm(forms.Form):
|
|||||||
if errors:
|
if errors:
|
||||||
raise forms.ValidationError(errors)
|
raise forms.ValidationError(errors)
|
||||||
|
|
||||||
def _validate_transaction(self):
|
def _validate_transaction(self) -> None:
|
||||||
"""Validates whether the transaction matches the transaction form.
|
"""Validates whether the transaction matches the transaction form.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -122,7 +123,7 @@ class RecordForm(forms.Form):
|
|||||||
_("This record is not for this transaction."),
|
_("This record is not for this transaction."),
|
||||||
code="not_belong")
|
code="not_belong")
|
||||||
|
|
||||||
def _validate_account_type(self):
|
def _validate_account_type(self) -> None:
|
||||||
"""Validates whether the account is a correct debit or credit account.
|
"""Validates whether the account is a correct debit or credit account.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -145,7 +146,7 @@ class RecordForm(forms.Form):
|
|||||||
self.add_error("account", error)
|
self.add_error("account", error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _validate_is_credit(self):
|
def _validate_is_credit(self) -> None:
|
||||||
"""Validates whether debit and credit records are submitted correctly
|
"""Validates whether debit and credit records are submitted correctly
|
||||||
as corresponding debit and credit records.
|
as corresponding debit and credit records.
|
||||||
|
|
||||||
@ -250,7 +251,7 @@ class TransactionForm(forms.Form):
|
|||||||
if errors:
|
if errors:
|
||||||
raise forms.ValidationError(errors)
|
raise forms.ValidationError(errors)
|
||||||
|
|
||||||
def _validate_has_debit_records(self):
|
def _validate_has_debit_records(self) -> None:
|
||||||
"""Validates whether there is any debit record.
|
"""Validates whether there is any debit record.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -268,7 +269,7 @@ class TransactionForm(forms.Form):
|
|||||||
_("Please fill in accounting records."),
|
_("Please fill in accounting records."),
|
||||||
code="has_debit_records")
|
code="has_debit_records")
|
||||||
|
|
||||||
def _validate_has_credit_records(self):
|
def _validate_has_credit_records(self) -> None:
|
||||||
"""Validates whether there is any credit record.
|
"""Validates whether there is any credit record.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -286,7 +287,7 @@ class TransactionForm(forms.Form):
|
|||||||
_("Please fill in accounting records."),
|
_("Please fill in accounting records."),
|
||||||
code="has_debit_records")
|
code="has_debit_records")
|
||||||
|
|
||||||
def _validate_balance(self):
|
def _validate_balance(self) -> None:
|
||||||
"""Validates whether the total amount of debit and credit records are
|
"""Validates whether the total amount of debit and credit records are
|
||||||
consistent.
|
consistent.
|
||||||
|
|
||||||
@ -301,20 +302,20 @@ class TransactionForm(forms.Form):
|
|||||||
_("The total of the debit and credit amounts are inconsistent."),
|
_("The total of the debit and credit amounts are inconsistent."),
|
||||||
code="balance")
|
code="balance")
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self) -> bool:
|
||||||
if not super(TransactionForm, self).is_valid():
|
if not super().is_valid():
|
||||||
return False
|
return False
|
||||||
for x in self.debit_records + self.credit_records:
|
for x in self.debit_records + self.credit_records:
|
||||||
if not x.is_valid():
|
if not x.is_valid():
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def balance_error(self):
|
def balance_error(self) -> Optional[str]:
|
||||||
"""Returns the error message when the transaction is imbalanced.
|
"""Returns the error message when the transaction is imbalanced.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The error message when the transaction is imbalanced, or
|
The error message when the transaction is imbalanced, or None
|
||||||
None otherwise.
|
otherwise.
|
||||||
"""
|
"""
|
||||||
errors = [x for x in self.non_field_errors().data
|
errors = [x for x in self.non_field_errors().data
|
||||||
if x.code == "balance"]
|
if x.code == "balance"]
|
||||||
@ -322,20 +323,20 @@ class TransactionForm(forms.Form):
|
|||||||
return errors[0].message
|
return errors[0].message
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def debit_total(self):
|
def debit_total(self) -> int:
|
||||||
"""Returns the total amount of the debit records.
|
"""Returns the total amount of the debit records.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: The total amount of the credit records.
|
The total amount of the credit records.
|
||||||
"""
|
"""
|
||||||
return sum([int(x.data["amount"]) for x in self.debit_records
|
return sum([int(x.data["amount"]) for x in self.debit_records
|
||||||
if "amount" in x.data and "amount" not in x.errors])
|
if "amount" in x.data and "amount" not in x.errors])
|
||||||
|
|
||||||
def credit_total(self):
|
def credit_total(self) -> int:
|
||||||
"""Returns the total amount of the credit records.
|
"""Returns the total amount of the credit records.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: The total amount of the credit records.
|
The total amount of the credit records.
|
||||||
"""
|
"""
|
||||||
return sum([int(x.data["amount"]) for x in self.credit_records
|
return sum([int(x.data["amount"]) for x in self.credit_records
|
||||||
if "amount" in x.data and "amount" not in x.errors])
|
if "amount" in x.data and "amount" not in x.errors])
|
||||||
@ -366,7 +367,8 @@ class AccountForm(forms.Form):
|
|||||||
self.account = None
|
self.account = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self):
|
def parent(self) -> Optional[Account]:
|
||||||
|
"""The parent account, or None if this is the topmost account."""
|
||||||
code = self["code"].value()
|
code = self["code"].value()
|
||||||
if code is None or len(code) < 2:
|
if code is None or len(code) < 2:
|
||||||
return None
|
return None
|
||||||
@ -391,7 +393,7 @@ class AccountForm(forms.Form):
|
|||||||
if errors:
|
if errors:
|
||||||
raise forms.ValidationError(errors)
|
raise forms.ValidationError(errors)
|
||||||
|
|
||||||
def _validate_code_not_under_myself(self):
|
def _validate_code_not_under_myself(self) -> None:
|
||||||
"""Validates whether the code is under itself.
|
"""Validates whether the code is under itself.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -411,7 +413,7 @@ class AccountForm(forms.Form):
|
|||||||
self.add_error("code", error)
|
self.add_error("code", error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _validate_code_unique(self):
|
def _validate_code_unique(self) -> None:
|
||||||
"""Validates whether the code is unique.
|
"""Validates whether the code is unique.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -432,7 +434,7 @@ class AccountForm(forms.Form):
|
|||||||
self.add_error("code", error)
|
self.add_error("code", error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _validate_code_parent_exists(self):
|
def _validate_code_parent_exists(self) -> None:
|
||||||
"""Validates whether the parent account exists.
|
"""Validates whether the parent account exists.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -452,7 +454,7 @@ class AccountForm(forms.Form):
|
|||||||
raise error
|
raise error
|
||||||
return
|
return
|
||||||
|
|
||||||
def _validate_code_descendant_code_size(self):
|
def _validate_code_descendant_code_size(self) -> None:
|
||||||
"""Validates whether the codes of the descendants will be too long.
|
"""Validates whether the codes of the descendants will be too long.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -467,7 +469,7 @@ class AccountForm(forms.Form):
|
|||||||
~Q(pk=self.account.pk))\
|
~Q(pk=self.account.pk))\
|
||||||
.aggregate(max_len=Max(Length("code")))["max_len"]
|
.aggregate(max_len=Max(Length("code")))["max_len"]
|
||||||
if cur_max_len is None:
|
if cur_max_len is None:
|
||||||
return True
|
return
|
||||||
new_max_len = cur_max_len - len(self.account.code)\
|
new_max_len = cur_max_len - len(self.account.code)\
|
||||||
+ len(self.data["code"])
|
+ len(self.data["code"])
|
||||||
if new_max_len <= 5:
|
if new_max_len <= 5:
|
||||||
|
@ -62,7 +62,7 @@ class Command(BaseCommand):
|
|||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
p = Populator(user)
|
p = Populator(user)
|
||||||
p.add_accounts((
|
p.add_accounts([
|
||||||
(1, "資產", "assets", "资产"),
|
(1, "資產", "assets", "资产"),
|
||||||
(2, "負債", "liabilities", "负债"),
|
(2, "負債", "liabilities", "负债"),
|
||||||
(3, "業主權益", "owners’ equity", "业主权益"),
|
(3, "業主權益", "owners’ equity", "业主权益"),
|
||||||
@ -124,7 +124,7 @@ class Command(BaseCommand):
|
|||||||
"管理及总务费用"),
|
"管理及总务费用"),
|
||||||
(6272, "伙食費", "meal (expenses)", "伙食费"),
|
(6272, "伙食費", "meal (expenses)", "伙食费"),
|
||||||
(6273, "職工福利", "employee benefits/welfare", "职工福利"),
|
(6273, "職工福利", "employee benefits/welfare", "职工福利"),
|
||||||
))
|
])
|
||||||
|
|
||||||
income = random.randint(40000, 50000)
|
income = random.randint(40000, 50000)
|
||||||
pension = 882 if income <= 40100\
|
pension = 882 if income <= 40100\
|
||||||
@ -143,49 +143,49 @@ class Command(BaseCommand):
|
|||||||
month = (date.replace(day=1) - timezone.timedelta(days=1)).month
|
month = (date.replace(day=1) - timezone.timedelta(days=1)).month
|
||||||
p.add_transfer_transaction(
|
p.add_transfer_transaction(
|
||||||
date,
|
date,
|
||||||
((1113, "薪資轉帳", savings),
|
[(1113, "薪資轉帳", savings),
|
||||||
(1314, F"勞保{month}月", pension),
|
(1314, F"勞保{month}月", pension),
|
||||||
(6262, F"健保{month}月", insurance),
|
(6262, F"健保{month}月", insurance),
|
||||||
(1255, "代扣所得稅", tax)),
|
(1255, "代扣所得稅", tax)],
|
||||||
((4611, F"{month}月份薪水", income),))
|
[(4611, F"{month}月份薪水", income)])
|
||||||
|
|
||||||
p.add_income_transaction(
|
p.add_income_transaction(
|
||||||
-15,
|
-15,
|
||||||
((1113, "ATM提款", 2000),))
|
[(1113, "ATM提款", 2000)])
|
||||||
p.add_transfer_transaction(
|
p.add_transfer_transaction(
|
||||||
-14,
|
-14,
|
||||||
((6254, "高鐵—台北→左營", 1490),),
|
[(6254, "高鐵—台北→左營", 1490)],
|
||||||
((2141, "高鐵—台北→左營", 1490),))
|
[(2141, "高鐵—台北→左營", 1490)])
|
||||||
p.add_transfer_transaction(
|
p.add_transfer_transaction(
|
||||||
-14,
|
-14,
|
||||||
((6273, "電影—復仇者聯盟", 80),),
|
[(6273, "電影—復仇者聯盟", 80)],
|
||||||
((2141, "電影—復仇者聯盟", 80),))
|
[(2141, "電影—復仇者聯盟", 80)])
|
||||||
p.add_transfer_transaction(
|
p.add_transfer_transaction(
|
||||||
-13,
|
-13,
|
||||||
((6273, "電影—2001太空漫遊", 80),),
|
[(6273, "電影—2001太空漫遊", 80)],
|
||||||
((2141, "電影—2001太空漫遊", 80),))
|
[(2141, "電影—2001太空漫遊", 80)])
|
||||||
p.add_transfer_transaction(
|
p.add_transfer_transaction(
|
||||||
-11,
|
-11,
|
||||||
((2141, "電影—復仇者聯盟", 80),),
|
[(2141, "電影—復仇者聯盟", 80)],
|
||||||
((1113, "電影—復仇者聯盟", 80),))
|
[(1113, "電影—復仇者聯盟", 80)])
|
||||||
|
|
||||||
p.add_expense_transaction(
|
p.add_expense_transaction(
|
||||||
-13,
|
-13,
|
||||||
((6273, "公車—262—民生社區→頂溪捷運站", 30),))
|
[(6273, "公車—262—民生社區→頂溪捷運站", 30)])
|
||||||
|
|
||||||
p.add_expense_transaction(
|
p.add_expense_transaction(
|
||||||
-2,
|
-2,
|
||||||
((6272, "午餐—排骨飯", random.randint(40, 200)),
|
[(6272, "午餐—排骨飯", random.randint(40, 200)),
|
||||||
(6272, "飲料—紅茶", random.randint(40, 200))))
|
(6272, "飲料—紅茶", random.randint(40, 200))])
|
||||||
p.add_expense_transaction(
|
p.add_expense_transaction(
|
||||||
-1,
|
-1,
|
||||||
((6272, "午餐—牛肉麵", random.randint(40, 200)),
|
([(6272, "午餐—牛肉麵", random.randint(40, 200)),
|
||||||
(6272, "飲料—紅茶", random.randint(40, 200))))
|
(6272, "飲料—紅茶", random.randint(40, 200))]))
|
||||||
p.add_expense_transaction(
|
p.add_expense_transaction(
|
||||||
-1,
|
-1,
|
||||||
((6272, "午餐—排骨飯", random.randint(40, 200)),
|
[(6272, "午餐—排骨飯", random.randint(40, 200)),
|
||||||
(6272, "飲料—冬瓜茶", random.randint(40, 200))))
|
(6272, "飲料—冬瓜茶", random.randint(40, 200))])
|
||||||
p.add_expense_transaction(
|
p.add_expense_transaction(
|
||||||
0,
|
0,
|
||||||
((6272, "午餐—雞腿飯", random.randint(40, 200)),
|
[(6272, "午餐—雞腿飯", random.randint(40, 200)),
|
||||||
(6272, "飲料—咖啡", random.randint(40, 200))))
|
(6272, "飲料—咖啡", random.randint(40, 200))])
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
"""The data models of the accounting application.
|
"""The data models of the accounting application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from dirtyfields import DirtyFieldsMixin
|
from dirtyfields import DirtyFieldsMixin
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
@ -83,51 +86,44 @@ class Account(DirtyFieldsMixin, models.Model):
|
|||||||
db_table = "accounting_accounts"
|
db_table = "accounting_accounts"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self) -> str:
|
||||||
|
"""The title in the current language."""
|
||||||
return get_multi_lingual_attr(self, "title")
|
return get_multi_lingual_attr(self, "title")
|
||||||
|
|
||||||
@title.setter
|
@title.setter
|
||||||
def title(self, value):
|
def title(self, value: str) -> None:
|
||||||
set_multi_lingual_attr(self, "title", value)
|
set_multi_lingual_attr(self, "title", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def option_data(self):
|
def option_data(self) -> Dict[str, str]:
|
||||||
|
"""The data as an option."""
|
||||||
return {
|
return {
|
||||||
"code": self.code,
|
"code": self.code,
|
||||||
"title": self.title,
|
"title": self.title,
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_parent_and_in_use(self):
|
def is_parent_and_in_use(self) -> bool:
|
||||||
"""Whether this is a parent account and is in use.
|
"""Whether this is a parent account and is in use."""
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if this is a parent account and is in use, or false
|
|
||||||
otherwise
|
|
||||||
"""
|
|
||||||
if self._is_parent_and_in_use is None:
|
if self._is_parent_and_in_use is None:
|
||||||
self._is_parent_and_in_use = self.child_set.count() > 0\
|
self._is_parent_and_in_use = self.child_set.count() > 0\
|
||||||
and self.record_set.count() > 0
|
and self.record_set.count() > 0
|
||||||
return self._is_parent_and_in_use
|
return self._is_parent_and_in_use
|
||||||
|
|
||||||
@is_parent_and_in_use.setter
|
@is_parent_and_in_use.setter
|
||||||
def is_parent_and_in_use(self, value):
|
def is_parent_and_in_use(self, value: bool) -> None:
|
||||||
self._is_parent_and_in_use = value
|
self._is_parent_and_in_use = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_in_use(self):
|
def is_in_use(self) -> bool:
|
||||||
"""Whether this account is in use.
|
"""Whether this account is in use."""
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if this account is in use, or false otherwise.
|
|
||||||
"""
|
|
||||||
if self._is_in_use is None:
|
if self._is_in_use is None:
|
||||||
self._is_in_use = self.child_set.count() > 0\
|
self._is_in_use = self.child_set.count() > 0\
|
||||||
or self.record_set.count() > 0
|
or self.record_set.count() > 0
|
||||||
return self._is_in_use
|
return self._is_in_use
|
||||||
|
|
||||||
@is_in_use.setter
|
@is_in_use.setter
|
||||||
def is_in_use(self, value):
|
def is_in_use(self, value: bool) -> None:
|
||||||
self._is_in_use = value
|
self._is_in_use = value
|
||||||
|
|
||||||
|
|
||||||
@ -157,7 +153,7 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
transaction."""
|
transaction."""
|
||||||
return self.date.__str__() + " #" + self.ord.__str__()
|
return self.date.__str__() + " #" + self.ord.__str__()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self) -> str:
|
||||||
"""Returns the URL to view this transaction."""
|
"""Returns the URL to view this transaction."""
|
||||||
if self.is_cash_expense:
|
if self.is_cash_expense:
|
||||||
return reverse(
|
return reverse(
|
||||||
@ -169,13 +165,13 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
return reverse(
|
return reverse(
|
||||||
"accounting:transactions.detail", args=("transfer", self))
|
"accounting:transactions.detail", args=("transfer", self))
|
||||||
|
|
||||||
def is_dirty(self, check_relationship=False, check_m2m=None):
|
def is_dirty(self, check_relationship=False, check_m2m=None) -> bool:
|
||||||
"""Returns whether the data of this transaction is changed and need
|
"""Returns whether the data of this transaction is changed and need
|
||||||
to be saved into the database.
|
to be saved into the database.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if the data of this transaction is changed and need
|
True if the data of this transaction is changed and need to be
|
||||||
to be saved into the database, or False otherwise.
|
saved into the database, or False otherwise.
|
||||||
"""
|
"""
|
||||||
if super().is_dirty(check_relationship=check_relationship,
|
if super().is_dirty(check_relationship=check_relationship,
|
||||||
check_m2m=check_m2m):
|
check_m2m=check_m2m):
|
||||||
@ -189,8 +185,9 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def save(self, current_user=None, old_date=None, force_insert=False,
|
def save(self, current_user=None, old_date: datetime.date = None,
|
||||||
force_update=False, using=None, update_fields=None):
|
force_insert=False, force_update=False, using=None,
|
||||||
|
update_fields=None):
|
||||||
# When the date is changed, the orders of the transactions in the same
|
# When the date is changed, the orders of the transactions in the same
|
||||||
# day need to be reordered
|
# day need to be reordered
|
||||||
txn_to_sort = []
|
txn_to_sort = []
|
||||||
@ -262,7 +259,7 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
"""The records of the transaction.
|
"""The records of the transaction.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[Record]: The records.
|
List[Record]: The records.
|
||||||
"""
|
"""
|
||||||
if self._records is None:
|
if self._records is None:
|
||||||
self._records = list(self.record_set.all())
|
self._records = list(self.record_set.all())
|
||||||
@ -278,22 +275,18 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
"""The debit records of this transaction.
|
"""The debit records of this transaction.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[Record]: The records.
|
List[Record]: The records.
|
||||||
"""
|
"""
|
||||||
return [x for x in self.records if not x.is_credit]
|
return [x for x in self.records if not x.is_credit]
|
||||||
|
|
||||||
def debit_total(self):
|
def debit_total(self) -> int:
|
||||||
"""The total amount of the debit records."""
|
"""The total amount of the debit records."""
|
||||||
return sum([x.amount for x in self.debit_records
|
return sum([x.amount for x in self.debit_records
|
||||||
if isinstance(x.amount, int)])
|
if isinstance(x.amount, int)])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def debit_summaries(self):
|
def debit_summaries(self) -> List[str]:
|
||||||
"""The summaries of the debit records.
|
"""The summaries of the debit records."""
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[str]: The summaries of the debit records.
|
|
||||||
"""
|
|
||||||
return [x.account.title if x.summary is None else x.summary
|
return [x.account.title if x.summary is None else x.summary
|
||||||
for x in self.debit_records]
|
for x in self.debit_records]
|
||||||
|
|
||||||
@ -302,36 +295,28 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
"""The credit records of this transaction.
|
"""The credit records of this transaction.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[Record]: The records.
|
List[Record]: The records.
|
||||||
"""
|
"""
|
||||||
return [x for x in self.records if x.is_credit]
|
return [x for x in self.records if x.is_credit]
|
||||||
|
|
||||||
def credit_total(self):
|
def credit_total(self) -> int:
|
||||||
"""The total amount of the credit records."""
|
"""The total amount of the credit records."""
|
||||||
return sum([x.amount for x in self.credit_records
|
return sum([x.amount for x in self.credit_records
|
||||||
if isinstance(x.amount, int)])
|
if isinstance(x.amount, int)])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def credit_summaries(self):
|
def credit_summaries(self) -> List[str]:
|
||||||
"""The summaries of the credit records.
|
"""The summaries of the credit records."""
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[str]: The summaries of the credit records.
|
|
||||||
"""
|
|
||||||
return [x.account.title if x.summary is None else x.summary
|
return [x.account.title if x.summary is None else x.summary
|
||||||
for x in self.credit_records]
|
for x in self.credit_records]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amount(self):
|
def amount(self) -> int:
|
||||||
"""The amount of this transaction.
|
"""The amount of this transaction."""
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: The amount of this transaction.
|
|
||||||
"""
|
|
||||||
return self.debit_total()
|
return self.debit_total()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_balanced(self):
|
def is_balanced(self) -> bool:
|
||||||
"""Whether the sum of the amounts of the debit records is the
|
"""Whether the sum of the amounts of the debit records is the
|
||||||
same as the sum of the amounts of the credit records. """
|
same as the sum of the amounts of the credit records. """
|
||||||
if self._is_balanced is None:
|
if self._is_balanced is None:
|
||||||
@ -341,16 +326,16 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
return self._is_balanced
|
return self._is_balanced
|
||||||
|
|
||||||
@is_balanced.setter
|
@is_balanced.setter
|
||||||
def is_balanced(self, value):
|
def is_balanced(self, value: bool) -> None:
|
||||||
self._is_balanced = value
|
self._is_balanced = value
|
||||||
|
|
||||||
def has_many_same_day(self):
|
def has_many_same_day(self) -> bool:
|
||||||
"""whether there are more than one transactions at this day,
|
"""whether there are more than one transactions at this day,
|
||||||
so that the user can sort their orders. """
|
so that the user can sort their orders. """
|
||||||
return Transaction.objects.filter(date=self.date).count() > 1
|
return Transaction.objects.filter(date=self.date).count() > 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_order_hole(self):
|
def has_order_hole(self) -> bool:
|
||||||
"""Whether the order of the transactions on this day is not
|
"""Whether the order of the transactions on this day is not
|
||||||
1, 2, 3, 4, 5..., and should be reordered. """
|
1, 2, 3, 4, 5..., and should be reordered. """
|
||||||
if self._has_order_hole is None:
|
if self._has_order_hole is None:
|
||||||
@ -369,11 +354,11 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
return self._has_order_hole
|
return self._has_order_hole
|
||||||
|
|
||||||
@has_order_hole.setter
|
@has_order_hole.setter
|
||||||
def has_order_hole(self, value):
|
def has_order_hole(self, value: bool) -> None:
|
||||||
self._has_order_hole = value
|
self._has_order_hole = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_cash_income(self):
|
def is_cash_income(self) -> bool:
|
||||||
"""Whether this transaction is a cash income transaction."""
|
"""Whether this transaction is a cash income transaction."""
|
||||||
debit_records = self.debit_records
|
debit_records = self.debit_records
|
||||||
return (len(debit_records) == 1
|
return (len(debit_records) == 1
|
||||||
@ -381,7 +366,7 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
and debit_records[0].summary is None)
|
and debit_records[0].summary is None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_cash_expense(self):
|
def is_cash_expense(self) -> bool:
|
||||||
"""Whether this transaction is a cash expense transaction."""
|
"""Whether this transaction is a cash expense transaction."""
|
||||||
credit_records = self.credit_records
|
credit_records = self.credit_records
|
||||||
return (len(credit_records) == 1
|
return (len(credit_records) == 1
|
||||||
@ -389,7 +374,7 @@ class Transaction(DirtyFieldsMixin, models.Model):
|
|||||||
and credit_records[0].summary is None)
|
and credit_records[0].summary is None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self) -> str:
|
||||||
"""The transaction type."""
|
"""The transaction type."""
|
||||||
if self.is_cash_expense:
|
if self.is_cash_expense:
|
||||||
return "expense"
|
return "expense"
|
||||||
@ -444,40 +429,40 @@ class Record(DirtyFieldsMixin, models.Model):
|
|||||||
db_table = "accounting_records"
|
db_table = "accounting_records"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def debit_amount(self):
|
def debit_amount(self) -> Optional[int]:
|
||||||
"""The debit amount of this accounting record."""
|
"""The debit amount of this accounting record."""
|
||||||
if self._debit_amount is None:
|
if self._debit_amount is None:
|
||||||
self._debit_amount = self.amount if not self.is_credit else None
|
self._debit_amount = self.amount if not self.is_credit else None
|
||||||
return self._debit_amount
|
return self._debit_amount
|
||||||
|
|
||||||
@debit_amount.setter
|
@debit_amount.setter
|
||||||
def debit_amount(self, value):
|
def debit_amount(self, value: Optional[int]) -> None:
|
||||||
self._debit_amount = value
|
self._debit_amount = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def credit_amount(self):
|
def credit_amount(self) -> Optional[int]:
|
||||||
"""The credit amount of this accounting record."""
|
"""The credit amount of this accounting record."""
|
||||||
if self._credit_amount is None:
|
if self._credit_amount is None:
|
||||||
self._credit_amount = self.amount if self.is_credit else None
|
self._credit_amount = self.amount if self.is_credit else None
|
||||||
return self._credit_amount
|
return self._credit_amount
|
||||||
|
|
||||||
@credit_amount.setter
|
@credit_amount.setter
|
||||||
def credit_amount(self, value):
|
def credit_amount(self, value: Optional[int]):
|
||||||
self._credit_amount = value
|
self._credit_amount = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_balanced(self):
|
def is_balanced(self) -> bool:
|
||||||
"""Whether the transaction of this record is balanced. """
|
"""Whether the transaction of this record is balanced. """
|
||||||
if self._is_balanced is None:
|
if self._is_balanced is None:
|
||||||
self._is_balanced = self.transaction.is_balanced
|
self._is_balanced = self.transaction.is_balanced
|
||||||
return self._is_balanced
|
return self._is_balanced
|
||||||
|
|
||||||
@is_balanced.setter
|
@is_balanced.setter
|
||||||
def is_balanced(self, value):
|
def is_balanced(self, value: bool) -> None:
|
||||||
self._is_balanced = value
|
self._is_balanced = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_order_hole(self):
|
def has_order_hole(self) -> bool:
|
||||||
"""Whether the order of the transactions on this day is not
|
"""Whether the order of the transactions on this day is not
|
||||||
1, 2, 3, 4, 5..., and should be reordered. """
|
1, 2, 3, 4, 5..., and should be reordered. """
|
||||||
if self._has_order_hole is None:
|
if self._has_order_hole is None:
|
||||||
@ -485,5 +470,5 @@ class Record(DirtyFieldsMixin, models.Model):
|
|||||||
return self._has_order_hole
|
return self._has_order_hole
|
||||||
|
|
||||||
@has_order_hole.setter
|
@has_order_hole.setter
|
||||||
def has_order_hole(self, value):
|
def has_order_hole(self, value: bool) -> None:
|
||||||
self._has_order_hole = value
|
self._has_order_hole = value
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
from typing import Union, Optional
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ register = template.Library()
|
|||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def accounting_amount(value):
|
def accounting_amount(value: Union[str, int]) -> str:
|
||||||
if value is None:
|
if value is None:
|
||||||
return ""
|
return ""
|
||||||
if value == 0:
|
if value == 0:
|
||||||
@ -47,13 +48,15 @@ def accounting_amount(value):
|
|||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def report_url(cash_account, ledger_account, period):
|
def report_url(cash_account: Optional[Account],
|
||||||
|
ledger_account: Optional[Account],
|
||||||
|
period: Optional[Period]) -> ReportUrl:
|
||||||
"""Returns accounting report URL helper.
|
"""Returns accounting report URL helper.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cash_account (Account): The current cash account.
|
cash_account: The current cash account.
|
||||||
ledger_account (Account): The current ledger account.
|
ledger_account: The current ledger account.
|
||||||
period (Period): The period.
|
period: The period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ReportUrl: The accounting report URL helper.
|
ReportUrl: The accounting report URL helper.
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
from typing import Union, Tuple, List, Optional, Iterable, Mapping, Dict
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
@ -37,6 +38,9 @@ from mia_core.utils import new_pk
|
|||||||
from .forms import TransactionForm, RecordForm
|
from .forms import TransactionForm, RecordForm
|
||||||
from .models import Account, Transaction, Record
|
from .models import Account, Transaction, Record
|
||||||
|
|
||||||
|
AccountData = Tuple[Union[str, int], str, str, str]
|
||||||
|
RecordData = Tuple[Union[str, int], Optional[str], int]
|
||||||
|
|
||||||
DEFAULT_CASH_ACCOUNT = "1111"
|
DEFAULT_CASH_ACCOUNT = "1111"
|
||||||
CASH_SHORTCUT_ACCOUNTS = ["0", "1111"]
|
CASH_SHORTCUT_ACCOUNTS = ["0", "1111"]
|
||||||
DEFAULT_LEDGER_ACCOUNT = "1111"
|
DEFAULT_LEDGER_ACCOUNT = "1111"
|
||||||
@ -48,12 +52,12 @@ class MonthlySummary:
|
|||||||
"""A summary record.
|
"""A summary record.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
month (datetime.date): The month.
|
month: The month.
|
||||||
label (str): The text label.
|
label: The text label.
|
||||||
credit (int): The credit amount.
|
credit: The credit amount.
|
||||||
debit (int): The debit amount.
|
debit: The debit amount.
|
||||||
balance (int): The balance.
|
balance: The balance.
|
||||||
cumulative_balance (int): The cumulative balance.
|
cumulative_balance: The cumulative balance.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
month (datetime.date): The month.
|
month (datetime.date): The month.
|
||||||
@ -64,8 +68,9 @@ class MonthlySummary:
|
|||||||
cumulative_balance (int): The cumulative balance.
|
cumulative_balance (int): The cumulative balance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, month=None, label=None, credit=None, debit=None,
|
def __init__(self, month: datetime.date = None, label: str = None,
|
||||||
balance=None, cumulative_balance=None):
|
credit: int = None, debit: int = None, balance: int = None,
|
||||||
|
cumulative_balance: int = None):
|
||||||
self.month = month
|
self.month = month
|
||||||
self.label = label
|
self.label = label
|
||||||
self.credit = credit
|
self.credit = credit
|
||||||
@ -80,43 +85,42 @@ class ReportUrl:
|
|||||||
"""The URL of the accounting reports.
|
"""The URL of the accounting reports.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cash (Account): The currently-specified account of the
|
cash: The currently-specified account of the
|
||||||
cash account or cash summary.
|
cash account or cash summary.
|
||||||
ledger (Account): The currently-specified account of the
|
ledger: The currently-specified account of the
|
||||||
ledger or leger summary.
|
ledger or leger summary.
|
||||||
period (Period): The currently-specified period.
|
period: The currently-specified period.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, cash=None, ledger=None, period=None):
|
def __init__(self, cash: Account = None, ledger: Account = None,
|
||||||
|
period: Period = None):
|
||||||
self._period = Period() if period is None else period
|
self._period = Period() if period is None else period
|
||||||
self._cash = get_default_cash_account() if cash is None else cash
|
self._cash = get_default_cash_account() if cash is None else cash
|
||||||
self._ledger = get_default_ledger_account()\
|
self._ledger = get_default_ledger_account()\
|
||||||
if ledger is None else ledger
|
if ledger is None else ledger
|
||||||
|
|
||||||
def cash(self):
|
def cash(self) -> str:
|
||||||
return reverse(
|
return reverse("accounting:cash", args=(self._cash, self._period))
|
||||||
"accounting:cash", args=(self._cash, self._period))
|
|
||||||
|
|
||||||
def cash_summary(self):
|
def cash_summary(self) -> str:
|
||||||
return reverse("accounting:cash-summary", args=(self._cash,))
|
return reverse("accounting:cash-summary", args=(self._cash,))
|
||||||
|
|
||||||
def ledger(self):
|
def ledger(self) -> str:
|
||||||
return reverse(
|
return reverse("accounting:ledger", args=(self._ledger, self._period))
|
||||||
"accounting:ledger", args=(self._ledger, self._period))
|
|
||||||
|
|
||||||
def ledger_summary(self):
|
def ledger_summary(self) -> str:
|
||||||
return reverse("accounting:ledger-summary", args=(self._ledger,))
|
return reverse("accounting:ledger-summary", args=(self._ledger,))
|
||||||
|
|
||||||
def journal(self):
|
def journal(self) -> str:
|
||||||
return reverse("accounting:journal", args=(self._period,))
|
return reverse("accounting:journal", args=(self._period,))
|
||||||
|
|
||||||
def trial_balance(self):
|
def trial_balance(self) -> str:
|
||||||
return reverse("accounting:trial-balance", args=(self._period,))
|
return reverse("accounting:trial-balance", args=(self._period,))
|
||||||
|
|
||||||
def income_statement(self):
|
def income_statement(self) -> str:
|
||||||
return reverse("accounting:income-statement", args=(self._period,))
|
return reverse("accounting:income-statement", args=(self._period,))
|
||||||
|
|
||||||
def balance_sheet(self):
|
def balance_sheet(self) -> str:
|
||||||
return reverse("accounting:balance-sheet", args=(self._period,))
|
return reverse("accounting:balance-sheet", args=(self._period,))
|
||||||
|
|
||||||
|
|
||||||
@ -124,7 +128,7 @@ class Populator:
|
|||||||
"""The helper to populate the accounting data.
|
"""The helper to populate the accounting data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user (User): The user in action.
|
user: The user in action.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
user (User): The user in action.
|
user (User): The user in action.
|
||||||
@ -133,7 +137,7 @@ class Populator:
|
|||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
self.user = user
|
self.user = user
|
||||||
|
|
||||||
def add_accounts(self, accounts):
|
def add_accounts(self, accounts: List[AccountData]) -> None:
|
||||||
"""Adds accounts.
|
"""Adds accounts.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -152,16 +156,16 @@ class Populator:
|
|||||||
title_zh_hans=data[3],
|
title_zh_hans=data[3],
|
||||||
created_by=self.user, updated_by=self.user).save()
|
created_by=self.user, updated_by=self.user).save()
|
||||||
|
|
||||||
def add_transfer_transaction(self, date, debit, credit):
|
def add_transfer_transaction(self, date: Union[datetime.date, int],
|
||||||
|
debit: List[RecordData],
|
||||||
|
credit: List[RecordData]) -> None:
|
||||||
"""Adds a transfer transaction.
|
"""Adds a transfer transaction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
date (datetime.date|int): The date, or the number of days from
|
date: The date, or the number of days from
|
||||||
today.
|
today.
|
||||||
debit (tuple[tuple[any]]): Tuples of (account, summary, amount)
|
debit: Tuples of (account, summary, amount) of the debit records.
|
||||||
of the debit records.
|
credit: Tuples of (account, summary, amount) of the credit records.
|
||||||
credit (tuple[tuple[any]]): Tuples of (account, summary, amount)
|
|
||||||
of the credit records.
|
|
||||||
"""
|
"""
|
||||||
if isinstance(date, int):
|
if isinstance(date, int):
|
||||||
date = timezone.localdate() + timezone.timedelta(days=date)
|
date = timezone.localdate() + timezone.timedelta(days=date)
|
||||||
@ -196,38 +200,36 @@ class Populator:
|
|||||||
updated_by=self.user)
|
updated_by=self.user)
|
||||||
order = order + 1
|
order = order + 1
|
||||||
|
|
||||||
def add_income_transaction(self, date, credit):
|
def add_income_transaction(self, date: Union[datetime.date, int],
|
||||||
|
credit: List[RecordData]) -> None:
|
||||||
"""Adds a cash income transaction.
|
"""Adds a cash income transaction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
date (datetime.date|int): The date, or the number of days from
|
date: The date, or the number of days from today.
|
||||||
today.
|
credit: Tuples of (account, summary, amount) of the credit records.
|
||||||
credit (tuple[tuple[any]]): Tuples of (account, summary, amount) of
|
|
||||||
the credit records.
|
|
||||||
"""
|
"""
|
||||||
amount = sum([x[2] for x in credit])
|
amount = sum([x[2] for x in credit])
|
||||||
self.add_transfer_transaction(
|
self.add_transfer_transaction(
|
||||||
date, ((Account.CASH, None, amount),), credit)
|
date, [(Account.CASH, None, amount)], credit)
|
||||||
|
|
||||||
def add_expense_transaction(self, date, debit):
|
def add_expense_transaction(self, date: Union[datetime.date, int],
|
||||||
|
debit: List[RecordData]) -> None:
|
||||||
"""Adds a cash income transaction.
|
"""Adds a cash income transaction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
date (datetime.date|int): The date, or the number of days from
|
date: The date, or the number of days from today.
|
||||||
today.
|
debit: Tuples of (account, summary, amount) of the debit records.
|
||||||
debit (tuple[tuple[any]]): Tuples of (account, summary, amount) of
|
|
||||||
the debit records.
|
|
||||||
"""
|
"""
|
||||||
amount = sum([x[2] for x in debit])
|
amount = sum([x[2] for x in debit])
|
||||||
self.add_transfer_transaction(
|
self.add_transfer_transaction(
|
||||||
date, debit, ((Account.CASH, None, amount),))
|
date, debit, [(Account.CASH, None, amount)])
|
||||||
|
|
||||||
|
|
||||||
def get_cash_accounts():
|
def get_cash_accounts() -> List[Account]:
|
||||||
"""Returns the cash accounts.
|
"""Returns the cash accounts.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[Account]: The cash accounts.
|
The cash accounts.
|
||||||
"""
|
"""
|
||||||
accounts = list(
|
accounts = list(
|
||||||
Account.objects
|
Account.objects
|
||||||
@ -247,11 +249,11 @@ def get_cash_accounts():
|
|||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
|
|
||||||
def get_default_cash_account():
|
def get_default_cash_account() -> Account:
|
||||||
"""Returns the default cash account.
|
"""Returns the default cash account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Account: The default cash account.
|
The default cash account.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
code = settings.ACCOUNTING["DEFAULT_CASH_ACCOUNT"]
|
code = settings.ACCOUNTING["DEFAULT_CASH_ACCOUNT"]
|
||||||
@ -274,11 +276,11 @@ def get_default_cash_account():
|
|||||||
return Account(code="0", title=_("current assets and liabilities"))
|
return Account(code="0", title=_("current assets and liabilities"))
|
||||||
|
|
||||||
|
|
||||||
def get_cash_shortcut_accounts():
|
def get_cash_shortcut_accounts() -> List[str]:
|
||||||
"""Returns the codes of the shortcut cash accounts.
|
"""Returns the codes of the shortcut cash accounts.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[str]: The codes of the shortcut cash accounts.
|
The codes of the shortcut cash accounts.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
accounts = settings.ACCOUNTING["CASH_SHORTCUT_ACCOUNTS"]
|
accounts = settings.ACCOUNTING["CASH_SHORTCUT_ACCOUNTS"]
|
||||||
@ -293,11 +295,11 @@ def get_cash_shortcut_accounts():
|
|||||||
return accounts
|
return accounts
|
||||||
|
|
||||||
|
|
||||||
def get_ledger_accounts():
|
def get_ledger_accounts() -> List[Account]:
|
||||||
"""Returns the accounts for the ledger.
|
"""Returns the accounts for the ledger.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[Account]: The accounts for the ledger.
|
The accounts for the ledger.
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
For SQL one-liner:
|
For SQL one-liner:
|
||||||
@ -315,18 +317,19 @@ SELECT s.*
|
|||||||
"""
|
"""
|
||||||
codes = {}
|
codes = {}
|
||||||
for code in [x.code for x in Account.objects
|
for code in [x.code for x in Account.objects
|
||||||
.annotate(Count("record")).filter(record__count__gt=0)]:
|
.annotate(Count("record"))
|
||||||
|
.filter(record__count__gt=0)]:
|
||||||
while len(code) > 0:
|
while len(code) > 0:
|
||||||
codes[code] = True
|
codes[code] = True
|
||||||
code = code[:-1]
|
code = code[:-1]
|
||||||
return Account.objects.filter(code__in=codes).order_by("code")
|
return Account.objects.filter(code__in=codes).order_by("code")
|
||||||
|
|
||||||
|
|
||||||
def get_default_ledger_account():
|
def get_default_ledger_account() -> Optional[Account]:
|
||||||
"""Returns the default ledger account.
|
"""Returns the default ledger account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Account: The default ledger account.
|
The default ledger account.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
code = settings.ACCOUNTING["DEFAULT_CASH_ACCOUNT"]
|
code = settings.ACCOUNTING["DEFAULT_CASH_ACCOUNT"]
|
||||||
@ -347,12 +350,12 @@ def get_default_ledger_account():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def find_imbalanced(records):
|
def find_imbalanced(records: Iterable[Record]) -> None:
|
||||||
""""Finds the records with imbalanced transactions, and sets their
|
""""Finds the records with imbalanced transactions, and sets their
|
||||||
is_balanced attribute.
|
is_balanced attribute.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
records (list[Record]): The accounting records.
|
records: The accounting records.
|
||||||
"""
|
"""
|
||||||
imbalanced = [x.pk for x in Transaction.objects
|
imbalanced = [x.pk for x in Transaction.objects
|
||||||
.annotate(
|
.annotate(
|
||||||
@ -364,13 +367,13 @@ def find_imbalanced(records):
|
|||||||
record.is_balanced = record.transaction.pk not in imbalanced
|
record.is_balanced = record.transaction.pk not in imbalanced
|
||||||
|
|
||||||
|
|
||||||
def find_order_holes(records):
|
def find_order_holes(records: Iterable[Record]) -> None:
|
||||||
""""Finds whether the order of the transactions on this day is not
|
""""Finds whether the order of the transactions on this day is not
|
||||||
1, 2, 3, 4, 5..., and should be reordered, and sets their
|
1, 2, 3, 4, 5..., and should be reordered, and sets their
|
||||||
has_order_holes attributes.
|
has_order_holes attributes.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
records (list[Record]): The accounting records.
|
records: The accounting records.
|
||||||
"""
|
"""
|
||||||
holes = [x["date"] for x in Transaction.objects
|
holes = [x["date"] for x in Transaction.objects
|
||||||
.values("date")
|
.values("date")
|
||||||
@ -387,12 +390,12 @@ def find_order_holes(records):
|
|||||||
and record.transaction.date in holes
|
and record.transaction.date in holes
|
||||||
|
|
||||||
|
|
||||||
def find_payable_records(account, records):
|
def find_payable_records(account: Account, records: Iterable[Record]) -> None:
|
||||||
"""Finds and sets the whether the payable record is paid.
|
"""Finds and sets the whether the payable record is paid.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
account (Account): The current ledger account.
|
account: The current ledger account.
|
||||||
records (list[Record]): The accounting records.
|
records: The accounting records.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
payable_accounts = settings.ACCOUNTING["PAYABLE_ACCOUNTS"]
|
payable_accounts = settings.ACCOUNTING["PAYABLE_ACCOUNTS"]
|
||||||
@ -422,12 +425,13 @@ def find_payable_records(account, records):
|
|||||||
x.is_payable = True
|
x.is_payable = True
|
||||||
|
|
||||||
|
|
||||||
def find_existing_equipments(account, records):
|
def find_existing_equipments(account: Account,
|
||||||
|
records: Iterable[Record]) -> None:
|
||||||
"""Finds and sets the equipments that still exist.
|
"""Finds and sets the equipments that still exist.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
account (Account): The current ledger account.
|
account: The current ledger account.
|
||||||
records (list[Record]): The accounting records.
|
records: The accounting records.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
equipment_accounts = settings.ACCOUNTING["EQUIPMENT_ACCOUNTS"]
|
equipment_accounts = settings.ACCOUNTING["EQUIPMENT_ACCOUNTS"]
|
||||||
@ -457,13 +461,13 @@ def find_existing_equipments(account, records):
|
|||||||
x.is_existing_equipment = True
|
x.is_existing_equipment = True
|
||||||
|
|
||||||
|
|
||||||
def get_summary_categories():
|
def get_summary_categories() -> str:
|
||||||
"""Finds and returns the summary categories and their corresponding account
|
"""Finds and returns the summary categories and their corresponding account
|
||||||
hints.
|
hints as JSON.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict[str,str]: The summary categories and their account hints, by
|
The summary categories and their account hints, by their record types
|
||||||
their record types and category types.
|
and category types.
|
||||||
"""
|
"""
|
||||||
rows = Record.objects\
|
rows = Record.objects\
|
||||||
.filter(Q(summary__contains="—"),
|
.filter(Q(summary__contains="—"),
|
||||||
@ -507,14 +511,15 @@ def get_summary_categories():
|
|||||||
return json.dumps(categories)
|
return json.dumps(categories)
|
||||||
|
|
||||||
|
|
||||||
def fill_txn_from_post(txn_type, txn, post):
|
def fill_txn_from_post(txn_type: str, txn: Transaction,
|
||||||
|
post: Mapping[str, str]) -> None:
|
||||||
"""Fills the transaction from the POSTed data. The POSTed data must be
|
"""Fills the transaction from the POSTed data. The POSTed data must be
|
||||||
validated and clean at this moment.
|
validated and clean at this moment.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
txn_type (str): The transaction type.
|
txn_type: The transaction type.
|
||||||
txn (Transaction): The transaction.
|
txn: The transaction.
|
||||||
post (dict): The POSTed data.
|
post: The POSTed data.
|
||||||
"""
|
"""
|
||||||
m = re.match("^([0-9]{4})-([0-9]{2})-([0-9]{2})$", post["date"])
|
m = re.match("^([0-9]{4})-([0-9]{2})-([0-9]{2})$", post["date"])
|
||||||
txn.date = datetime.date(
|
txn.date = datetime.date(
|
||||||
@ -565,12 +570,12 @@ def fill_txn_from_post(txn_type, txn, post):
|
|||||||
txn.records = records
|
txn.records = records
|
||||||
|
|
||||||
|
|
||||||
def sort_post_txn_records(post):
|
def sort_post_txn_records(post: Dict[str, str]) -> None:
|
||||||
"""Sorts the records in the form by their specified order, so that the
|
"""Sorts the records in the form by their specified order, so that the
|
||||||
form can be used to populate the data to return to the user.
|
form can be used to populate the data to return to the user.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
post (dict): The POSTed form.
|
post: The POSTed form.
|
||||||
"""
|
"""
|
||||||
# Collects the available record numbers
|
# Collects the available record numbers
|
||||||
record_no = {
|
record_no = {
|
||||||
@ -618,15 +623,16 @@ def sort_post_txn_records(post):
|
|||||||
post[key] = new_post[key]
|
post[key] = new_post[key]
|
||||||
|
|
||||||
|
|
||||||
def make_txn_form_from_model(txn_type, txn):
|
def make_txn_form_from_model(txn_type: str,
|
||||||
|
txn: Transaction) -> TransactionForm:
|
||||||
"""Converts a transaction data model to a transaction form.
|
"""Converts a transaction data model to a transaction form.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
txn_type (str): The transaction type.
|
txn_type: The transaction type.
|
||||||
txn (Transaction): The transaction data model.
|
txn: The transaction data model.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TransactionForm: The transaction form.
|
The transaction form.
|
||||||
"""
|
"""
|
||||||
form = TransactionForm(
|
form = TransactionForm(
|
||||||
{x: str(getattr(txn, x)) for x in ["date", "notes"]
|
{x: str(getattr(txn, x)) for x in ["date", "notes"]
|
||||||
@ -658,7 +664,8 @@ def make_txn_form_from_model(txn_type, txn):
|
|||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
def _find_max_record_no(txn_type, post):
|
def _find_max_record_no(txn_type: str,
|
||||||
|
post: Mapping[str, str]) -> Dict[str, int]:
|
||||||
"""Finds the max debit and record numbers from the POSTed form.
|
"""Finds the max debit and record numbers from the POSTed form.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -25,11 +25,11 @@ from django.utils.translation import gettext as _
|
|||||||
from .models import Account, Record
|
from .models import Account, Record
|
||||||
|
|
||||||
|
|
||||||
def validate_record_id(value):
|
def validate_record_id(value: str) -> None:
|
||||||
"""Validates the record ID.
|
"""Validates the record ID.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (str): The record ID.
|
value: The record ID.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError: When the validation fails.
|
ValidationError: When the validation fails.
|
||||||
@ -41,11 +41,11 @@ def validate_record_id(value):
|
|||||||
code="not_exist")
|
code="not_exist")
|
||||||
|
|
||||||
|
|
||||||
def validate_record_account_code(value):
|
def validate_record_account_code(value: str) -> None:
|
||||||
"""Validates an account code.
|
"""Validates an account code.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (str): The account code.
|
value: The account code.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError: When the validation fails.
|
ValidationError: When the validation fails.
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"""The view controllers of the accounting application.
|
"""The view controllers of the accounting application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -26,7 +27,8 @@ from django.db import transaction
|
|||||||
from django.db.models import Sum, Case, When, F, Q, Count, BooleanField, \
|
from django.db.models import Sum, Case, When, F, Q, Count, BooleanField, \
|
||||||
ExpressionWrapper
|
ExpressionWrapper
|
||||||
from django.db.models.functions import TruncMonth, Coalesce
|
from django.db.models.functions import TruncMonth, Coalesce
|
||||||
from django.http import JsonResponse, HttpResponseRedirect, Http404
|
from django.http import JsonResponse, HttpResponseRedirect, Http404, \
|
||||||
|
HttpRequest, HttpResponse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@ -62,16 +64,17 @@ class CashDefaultView(RedirectView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def cash(request, account, period):
|
def cash(request: HttpRequest, account: Account,
|
||||||
|
period: Period) -> HttpResponse:
|
||||||
"""The cash account.
|
"""The cash account.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
account (Account): The account.
|
account: The account.
|
||||||
period (Period): The period.
|
period: The period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The accounting records
|
# The accounting records
|
||||||
if account.code == "0":
|
if account.code == "0":
|
||||||
@ -180,15 +183,15 @@ class CashSummaryDefaultView(RedirectView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def cash_summary(request, account):
|
def cash_summary(request: HttpRequest, account: Account) -> HttpResponse:
|
||||||
"""The cash account summary.
|
"""The cash account summary.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
account (Account): The account.
|
account: The account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The account
|
# The account
|
||||||
accounts = utils.get_cash_accounts()
|
accounts = utils.get_cash_accounts()
|
||||||
@ -278,16 +281,17 @@ class LedgerDefaultView(RedirectView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def ledger(request, account, period):
|
def ledger(request: HttpRequest, account: Account,
|
||||||
|
period: Period) -> HttpResponse:
|
||||||
"""The ledger.
|
"""The ledger.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
account (Account): The account.
|
account: The account.
|
||||||
period (Period): The period.
|
period: The period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The accounting records
|
# The accounting records
|
||||||
records = list(
|
records = list(
|
||||||
@ -354,15 +358,15 @@ class LedgerSummaryDefaultView(RedirectView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def ledger_summary(request, account):
|
def ledger_summary(request: HttpRequest, account: Account) -> HttpResponse:
|
||||||
"""The ledger summary report.
|
"""The ledger summary report.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
account (Account): The account.
|
account: The account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The month summaries
|
# The month summaries
|
||||||
months = [utils.MonthlySummary(**x) for x in Record.objects
|
months = [utils.MonthlySummary(**x) for x in Record.objects
|
||||||
@ -416,15 +420,15 @@ class JournalDefaultView(RedirectView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def journal(request, period):
|
def journal(request: HttpRequest, period: Period) -> HttpResponse:
|
||||||
"""The journal.
|
"""The journal.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
period (Period): The period.
|
period: The period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The accounting records
|
# The accounting records
|
||||||
records = Record.objects \
|
records = Record.objects \
|
||||||
@ -498,15 +502,15 @@ class TrialBalanceDefaultView(RedirectView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def trial_balance(request, period):
|
def trial_balance(request: HttpRequest, period: Period) -> HttpResponse:
|
||||||
"""The trial balance.
|
"""The trial balance.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
period (Period): The period.
|
period: The period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The accounts
|
# The accounts
|
||||||
nominal = list(
|
nominal = list(
|
||||||
@ -601,15 +605,15 @@ class IncomeStatementDefaultView(RedirectView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def income_statement(request, period):
|
def income_statement(request: HttpRequest, period: Period) -> HttpResponse:
|
||||||
"""The income statement.
|
"""The income statement.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
period (Period): The period.
|
period: The period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The accounts
|
# The accounts
|
||||||
accounts = list(
|
accounts = list(
|
||||||
@ -676,15 +680,15 @@ class BalanceSheetDefaultView(RedirectView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def balance_sheet(request, period):
|
def balance_sheet(request: HttpRequest, period: Period) -> HttpResponse:
|
||||||
"""The balance sheet.
|
"""The balance sheet.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
period (Period): The period.
|
period: The period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The accounts
|
# The accounts
|
||||||
accounts = list(
|
accounts = list(
|
||||||
@ -764,14 +768,14 @@ def balance_sheet(request, period):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def search(request):
|
def search(request: HttpRequest) -> HttpResponse:
|
||||||
"""The search.
|
"""The search.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest) The request.
|
request: The request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
# The accounting records
|
# The accounting records
|
||||||
query = request.GET.get("q")
|
query = request.GET.get("q")
|
||||||
@ -809,16 +813,17 @@ class TransactionView(DetailView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def txn_form(request, txn_type, txn=None):
|
def txn_form(request: HttpRequest, txn_type: str,
|
||||||
|
txn: Transaction = None) -> HttpResponse:
|
||||||
"""The view to edit an accounting transaction.
|
"""The view to edit an accounting transaction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
txn_type (str): The transaction type.
|
txn_type: The transaction type.
|
||||||
txn (Transaction): The transaction.
|
txn: The transaction.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
previous_post = stored_post.get_previous_post(request)
|
previous_post = stored_post.get_previous_post(request)
|
||||||
if previous_post is not None:
|
if previous_post is not None:
|
||||||
@ -847,16 +852,17 @@ def txn_form(request, txn_type, txn=None):
|
|||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
def txn_store(request, txn_type, txn=None):
|
def txn_store(request: HttpRequest, txn_type: str,
|
||||||
|
txn: Transaction = None) -> HttpResponseRedirect:
|
||||||
"""The view to store an accounting transaction.
|
"""The view to store an accounting transaction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
txn_type (str): The transaction type.
|
txn_type: The transaction type.
|
||||||
txn (Transaction): The transaction.
|
txn: The transaction.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
post = request.POST.dict()
|
post = request.POST.dict()
|
||||||
strip_post(post)
|
strip_post(post)
|
||||||
@ -900,15 +906,15 @@ class TransactionDeleteView(DeleteView):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def txn_sort(request, date):
|
def txn_sort(request: HttpRequest, date: datetime.date) -> HttpResponse:
|
||||||
"""The view for the form to sort the transactions in a same day.
|
"""The view for the form to sort the transactions in a same day.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
date (datetime.date): The day.
|
date: The day.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
Http404: When there are less than two transactions in this day.
|
Http404: When there are less than two transactions in this day.
|
||||||
@ -977,15 +983,16 @@ class AccountView(DetailView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def account_form(request, account=None):
|
def account_form(request: HttpRequest,
|
||||||
|
account: Account = None) -> HttpResponse:
|
||||||
"""The view to edit an accounting transaction.
|
"""The view to edit an accounting transaction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
account (Account): The account.
|
account: The account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
previous_post = stored_post.get_previous_post(request)
|
previous_post = stored_post.get_previous_post(request)
|
||||||
if previous_post is not None:
|
if previous_post is not None:
|
||||||
@ -1005,15 +1012,16 @@ def account_form(request, account=None):
|
|||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
def account_store(request, account=None):
|
def account_store(request: HttpRequest,
|
||||||
|
account: Account = None) -> HttpResponseRedirect:
|
||||||
"""The view to store an account.
|
"""The view to store an account.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
account (Account): The account.
|
account: The account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponseRedirect: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
post = request.POST.dict()
|
post = request.POST.dict()
|
||||||
strip_post(post)
|
strip_post(post)
|
||||||
@ -1040,7 +1048,8 @@ def account_store(request, account=None):
|
|||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
def account_delete(request, account):
|
def account_delete(request: HttpRequest,
|
||||||
|
account: Account) -> HttpResponseRedirect:
|
||||||
"""The view to delete an account.
|
"""The view to delete an account.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -1062,28 +1071,28 @@ def account_delete(request, account):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def api_account_list(request):
|
def api_account_list(request: HttpRequest) -> JsonResponse:
|
||||||
"""The API view to return all the accounts.
|
"""The API view to return all the accounts.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
JsonResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
return JsonResponse({x.code: x.title for x in Account.objects.all()})
|
return JsonResponse({x.code: x.title for x in Account.objects.all()})
|
||||||
|
|
||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def api_account_options(request):
|
def api_account_options(request: HttpRequest) -> JsonResponse:
|
||||||
"""The API view to return the account options.
|
"""The API view to return the account options.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
JsonResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
accounts = Account.objects\
|
accounts = Account.objects\
|
||||||
.annotate(children_count=Count("child_set"))\
|
.annotate(children_count=Count("child_set"))\
|
||||||
|
Loading…
Reference in New Issue
Block a user