2020-07-22 00:15:26 +08:00
|
|
|
# The accounting application of the Mia project.
|
|
|
|
# by imacat <imacat@mail.imacat.idv.tw>, 2020/7/22
|
|
|
|
|
|
|
|
# Copyright (c) 2020 imacat.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
"""The command to populate the database with sample accounting data.
|
|
|
|
|
|
|
|
"""
|
2020-08-18 22:50:36 +08:00
|
|
|
import datetime
|
2020-08-31 23:04:03 +08:00
|
|
|
import getpass
|
2020-07-22 08:17:20 +08:00
|
|
|
import random
|
2020-08-19 19:19:37 +08:00
|
|
|
from typing import Optional
|
2020-07-22 08:17:20 +08:00
|
|
|
|
2020-08-18 03:05:47 +08:00
|
|
|
from django.contrib.auth import get_user_model
|
2020-08-27 22:06:46 +08:00
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
2020-09-01 10:21:05 +08:00
|
|
|
from django.core.management import BaseCommand, CommandParser, CommandError, \
|
|
|
|
call_command
|
2020-08-02 18:23:01 +08:00
|
|
|
from django.db import transaction
|
2020-08-27 23:09:17 +08:00
|
|
|
from django.utils import timezone, formats
|
|
|
|
from django.utils.translation import gettext as _
|
2020-07-22 00:15:26 +08:00
|
|
|
|
2020-09-01 09:12:45 +08:00
|
|
|
from accounting.models import Account, Record
|
2020-08-19 19:19:37 +08:00
|
|
|
from accounting.utils import DataFiller
|
2020-07-22 00:15:26 +08:00
|
|
|
|
|
|
|
|
|
|
|
class Command(BaseCommand):
|
|
|
|
"""Populates the database with sample accounting data."""
|
2020-08-19 19:19:37 +08:00
|
|
|
help = "Fills the database with sample accounting data."
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
super().__init__()
|
|
|
|
self._filler: Optional[DataFiller] = None
|
2020-07-22 00:15:26 +08:00
|
|
|
|
|
|
|
def add_arguments(self, parser):
|
|
|
|
"""Adds command line arguments to the parser.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
parser (CommandParser): The command line argument parser.
|
|
|
|
"""
|
2020-08-27 22:06:46 +08:00
|
|
|
parser.add_argument("--user", "-u", help="User")
|
2020-07-22 00:15:26 +08:00
|
|
|
|
|
|
|
def handle(self, *args, **options):
|
|
|
|
"""Runs the command.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
*args (list[str]): The command line arguments.
|
|
|
|
**options (dict[str,str]): The command line switches.
|
|
|
|
"""
|
2020-09-01 09:12:45 +08:00
|
|
|
if Record.objects.count() > 0:
|
2020-08-27 22:06:46 +08:00
|
|
|
error = "Refuse to fill in sample data with existing data."
|
|
|
|
raise CommandError(error, returncode=1)
|
|
|
|
# Gets the user to use
|
2020-09-01 09:12:45 +08:00
|
|
|
user = self.get_user(options["user"])
|
2020-09-01 10:21:05 +08:00
|
|
|
if Account.objects.count() == 0:
|
|
|
|
username = getattr(user, user.USERNAME_FIELD)
|
|
|
|
call_command("accounting_accounts", F"-u={username}")
|
2020-08-31 23:04:03 +08:00
|
|
|
self.stdout.write(F"Filling sample data as \"{user}\"")
|
2020-08-02 18:23:01 +08:00
|
|
|
|
2020-08-27 22:06:46 +08:00
|
|
|
with transaction.atomic():
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler = DataFiller(user)
|
2020-08-18 22:50:36 +08:00
|
|
|
self.add_payrolls(5)
|
|
|
|
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_income_transaction(
|
2020-08-02 18:23:01 +08:00
|
|
|
-15,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(1113, _("ATM withdrawal"), 2000)])
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_transfer_transaction(
|
2020-08-02 18:23:01 +08:00
|
|
|
-14,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(6254, _("HSR—New Land→South Lake City"), 1490)],
|
|
|
|
[(2141, _("HSR—New Land→South Lake City"), 1490)])
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_transfer_transaction(
|
2020-08-02 18:23:01 +08:00
|
|
|
-14,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(6273, _("Movies—The Avengers"), 80)],
|
|
|
|
[(2141, _("Movies—The Avengers"), 80)])
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_transfer_transaction(
|
2020-08-04 09:56:02 +08:00
|
|
|
-13,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(6273, _("Movies—2001: A Space Odyssey"), 80)],
|
|
|
|
[(2141, _("Movies—2001: A Space Odyssey"), 80)])
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_transfer_transaction(
|
2020-08-02 18:23:01 +08:00
|
|
|
-11,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(2141, _("Movies—The Avengers"), 80)],
|
|
|
|
[(1113, _("Movies—The Avengers"), 80)])
|
2020-08-02 18:23:01 +08:00
|
|
|
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_expense_transaction(
|
2020-08-04 09:56:02 +08:00
|
|
|
-13,
|
2020-09-01 11:25:26 +08:00
|
|
|
[(6273, _("Bus—2623—Uptown→City Park"), 15.5)])
|
2020-08-04 09:56:02 +08:00
|
|
|
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_expense_transaction(
|
2020-08-02 18:23:01 +08:00
|
|
|
-2,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(6272, _("Lunch—Spaghetti"), random.randint(40, 200)),
|
|
|
|
(6272, _("Drink—Tea"), random.randint(40, 200))])
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_expense_transaction(
|
2020-08-02 18:23:01 +08:00
|
|
|
-1,
|
2020-08-27 23:09:17 +08:00
|
|
|
([(6272, _("Lunch—Pizza"), random.randint(40, 200)),
|
|
|
|
(6272, _("Drink—Tea"), random.randint(40, 200))]))
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_expense_transaction(
|
2020-08-02 18:23:01 +08:00
|
|
|
-1,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(6272, _("Lunch—Spaghetti"), random.randint(40, 200)),
|
|
|
|
(6272, _("Drink—Soda"), random.randint(40, 200))])
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_expense_transaction(
|
2020-08-02 18:23:01 +08:00
|
|
|
0,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(6272, _("Lunch—Salad"), random.randint(40, 200)),
|
|
|
|
(6272, _("Drink—Coffee"), random.randint(40, 200))])
|
2020-08-18 22:50:36 +08:00
|
|
|
|
2020-09-01 09:12:45 +08:00
|
|
|
@staticmethod
|
|
|
|
def get_user(username_option):
|
|
|
|
"""Returns the current user.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
username_option: The username specified in the options.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The current user.
|
|
|
|
"""
|
|
|
|
user_model = get_user_model()
|
|
|
|
if username_option is not None:
|
|
|
|
try:
|
|
|
|
return user_model.objects.get(**{
|
|
|
|
user_model.USERNAME_FIELD: username_option
|
|
|
|
})
|
|
|
|
except ObjectDoesNotExist:
|
|
|
|
error = F"User \"{username_option}\" does not exist."
|
|
|
|
raise CommandError(error, returncode=1)
|
|
|
|
if user_model.objects.count() == 0:
|
2020-09-02 09:58:02 +08:00
|
|
|
call_command("createsuperuser")
|
|
|
|
return user_model.objects.first()
|
2020-09-01 09:12:45 +08:00
|
|
|
if user_model.objects.count() == 1:
|
|
|
|
return user_model.objects.first()
|
|
|
|
try:
|
|
|
|
return user_model.objects.get(**{
|
|
|
|
user_model.USERNAME_FIELD: getpass.getuser()
|
|
|
|
})
|
|
|
|
except ObjectDoesNotExist:
|
|
|
|
error = "Please specify the user with -u."
|
|
|
|
raise CommandError(error, returncode=1)
|
|
|
|
|
2020-08-18 22:50:36 +08:00
|
|
|
def add_payrolls(self, months: int):
|
|
|
|
"""Adds the payrolls for certain number of months.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
months: The number of months to add.
|
|
|
|
"""
|
|
|
|
today = timezone.localdate()
|
|
|
|
payday = today.replace(day=5)
|
|
|
|
if payday > today:
|
|
|
|
payday = self.previous_month(payday)
|
|
|
|
for i in range(months):
|
|
|
|
self.add_payroll(payday)
|
|
|
|
payday = self.previous_month(payday)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def previous_month(date: datetime.date):
|
|
|
|
"""Obtain the same day in the previous month.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
date: The date.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The same day in the previous month.
|
|
|
|
"""
|
|
|
|
month = date.month - 1
|
|
|
|
if month < 1:
|
|
|
|
year = date.year - 1
|
|
|
|
return date.replace(year=year, month=12)
|
|
|
|
return date.replace(month=month)
|
|
|
|
|
|
|
|
def add_payroll(self, payday: datetime.date):
|
|
|
|
"""Adds the payroll for a payday.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
payday: The payday.
|
|
|
|
"""
|
|
|
|
income = random.randint(40000, 50000)
|
|
|
|
pension = 882 if income <= 40100\
|
|
|
|
else 924 if income <= 42000\
|
|
|
|
else 966 if income <= 43900\
|
|
|
|
else 1008
|
|
|
|
insurance = 564 if income <= 40100\
|
|
|
|
else 591 if income <= 42000\
|
|
|
|
else 618 if income <= 43900\
|
|
|
|
else 644 if income <= 45800\
|
|
|
|
else 678 if income <= 48200\
|
|
|
|
else 712
|
|
|
|
tax = round(income * 0.05)
|
|
|
|
savings = income - pension - insurance - tax
|
2020-08-27 23:09:17 +08:00
|
|
|
previous_month = self.previous_month(payday)
|
|
|
|
month = formats.date_format(previous_month, format="F")
|
2020-08-19 19:19:37 +08:00
|
|
|
self._filler.add_transfer_transaction(
|
2020-08-18 22:50:36 +08:00
|
|
|
payday,
|
2020-08-27 23:09:17 +08:00
|
|
|
[(1113, _("Payroll Transfer"), savings),
|
|
|
|
(1314, _("Pension for {month}").format(month=month), pension),
|
|
|
|
(6262, _("Health insurance for {month}").format(month=month),
|
|
|
|
insurance),
|
|
|
|
(1255, _("Income Tax"), tax)],
|
|
|
|
[(4611, _("Payroll for {month}").format(month=month), income)])
|