Moved the sample data generation from the make-sample.py script to the test site. The sample data is generated at real time. This avoids the problem with pre-recorded sample data that the beginning of the months and weeks changes with the day resetting the sample data.
This commit is contained in:
parent
7bcc2b28b2
commit
bbc78433fd
@ -1,368 +0,0 @@
|
|||||||
#! env python3
|
|
||||||
# The Mia! Accounting Project.
|
|
||||||
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/4/9
|
|
||||||
|
|
||||||
# Copyright (c) 2023 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 sample data generation.
|
|
||||||
|
|
||||||
"""
|
|
||||||
import csv
|
|
||||||
import typing as t
|
|
||||||
from datetime import date, timedelta
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import click
|
|
||||||
|
|
||||||
from test_site.lib import JournalEntryLineItemData, JournalEntryCurrencyData, \
|
|
||||||
JournalEntryData, BaseTestData
|
|
||||||
from testlib import Accounts, create_test_app
|
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
|
||||||
def main() -> None:
|
|
||||||
"""Creates the sample data and output to a file."""
|
|
||||||
data: SampleData = SampleData(create_test_app(), "editor")
|
|
||||||
data_dir: Path = Path(__file__).parent / "test_site" / "data"
|
|
||||||
data.write_journal_entries(data_dir / "sample-journal_entries.csv")
|
|
||||||
data.write_line_items(data_dir / "sample-journal_entry_line_items.csv")
|
|
||||||
|
|
||||||
|
|
||||||
class SampleData(BaseTestData):
|
|
||||||
"""The sample data."""
|
|
||||||
|
|
||||||
def _init_data(self) -> None:
|
|
||||||
self.__add_recurring()
|
|
||||||
self.__add_offsets()
|
|
||||||
self.__add_meals()
|
|
||||||
|
|
||||||
def __add_recurring(self) -> None:
|
|
||||||
"""Adds the recurring data.
|
|
||||||
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
self.__add_usd_recurring()
|
|
||||||
self.__add_twd_recurring()
|
|
||||||
|
|
||||||
def __add_usd_recurring(self) -> None:
|
|
||||||
"""Adds the recurring data in USD.
|
|
||||||
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
today: date = date.today()
|
|
||||||
days: int
|
|
||||||
year: int
|
|
||||||
month: int
|
|
||||||
|
|
||||||
# Recurring in USD
|
|
||||||
j_date: date = date(today.year - 5, today.month, today.day)
|
|
||||||
j_date = j_date + timedelta(days=(4 - j_date.weekday()))
|
|
||||||
days = (today - j_date).days
|
|
||||||
while True:
|
|
||||||
if days < 0:
|
|
||||||
break
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "USD", "2600",
|
|
||||||
Accounts.BANK, "Transfer", Accounts.SERVICE, "Payroll")
|
|
||||||
|
|
||||||
days = days - 1
|
|
||||||
if days < 0:
|
|
||||||
break
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "USD", "1200",
|
|
||||||
Accounts.CASH, None, Accounts.BANK, "Withdraw")
|
|
||||||
days = days - 13
|
|
||||||
|
|
||||||
year = today.year - 5
|
|
||||||
month = today.month
|
|
||||||
while True:
|
|
||||||
month = month + 1
|
|
||||||
if month > 12:
|
|
||||||
year = year + 1
|
|
||||||
month = 1
|
|
||||||
days = (today - date(year, month, 1)).days
|
|
||||||
if days < 0:
|
|
||||||
break
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "USD", "1800",
|
|
||||||
Accounts.RENT_EXPENSE, "Rent", Accounts.BANK, "Transfer")
|
|
||||||
|
|
||||||
def __add_twd_recurring(self) -> None:
|
|
||||||
"""Adds the recurring data in TWD.
|
|
||||||
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
today: date = date.today()
|
|
||||||
|
|
||||||
year: int = today.year - 5
|
|
||||||
month: int = today.month
|
|
||||||
while True:
|
|
||||||
days: int = (today - date(year, month, 5)).days
|
|
||||||
if days < 0:
|
|
||||||
break
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "TWD", "50000",
|
|
||||||
Accounts.BANK, "薪資轉帳", Accounts.SERVICE, "薪水")
|
|
||||||
|
|
||||||
days = days - 1
|
|
||||||
if days < 0:
|
|
||||||
break
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "TWD", "25000",
|
|
||||||
Accounts.CASH, None, Accounts.BANK, "提款")
|
|
||||||
|
|
||||||
days = days - 4
|
|
||||||
if days < 0:
|
|
||||||
break
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "TWD", "18000",
|
|
||||||
Accounts.RENT_EXPENSE, "房租", Accounts.BANK, "轉帳")
|
|
||||||
|
|
||||||
month = month + 1
|
|
||||||
if month > 12:
|
|
||||||
year = year + 1
|
|
||||||
month = 1
|
|
||||||
|
|
||||||
def __add_offsets(self) -> None:
|
|
||||||
"""Adds the offset data.
|
|
||||||
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
days: int
|
|
||||||
year: int
|
|
||||||
month: int
|
|
||||||
description: str
|
|
||||||
line_item_or: JournalEntryLineItemData
|
|
||||||
line_item_of: JournalEntryLineItemData
|
|
||||||
|
|
||||||
# Full offset and unmatched in USD
|
|
||||||
description = "Speaking—Institute"
|
|
||||||
line_item_or = JournalEntryLineItemData(
|
|
||||||
Accounts.RECEIVABLE, description, "120")
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
40, [JournalEntryCurrencyData(
|
|
||||||
"USD", [line_item_or], [JournalEntryLineItemData(
|
|
||||||
Accounts.SERVICE, description, "120")])]))
|
|
||||||
line_item_of = JournalEntryLineItemData(
|
|
||||||
Accounts.RECEIVABLE, description, "120",
|
|
||||||
original_line_item=line_item_or)
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
5, [JournalEntryCurrencyData(
|
|
||||||
"USD", [JournalEntryLineItemData(
|
|
||||||
Accounts.BANK, description, "120")],
|
|
||||||
[line_item_of])]))
|
|
||||||
self.__add_journal_entry(
|
|
||||||
30, "USD", "120",
|
|
||||||
Accounts.BANK, description, Accounts.SERVICE, description)
|
|
||||||
|
|
||||||
# Partial offset in USD
|
|
||||||
line_item_or = JournalEntryLineItemData(
|
|
||||||
Accounts.PAYABLE, "Computer", "1600")
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
60, [JournalEntryCurrencyData(
|
|
||||||
"USD", [JournalEntryLineItemData(
|
|
||||||
Accounts.MACHINERY, "Computer", "1600")],
|
|
||||||
[line_item_or])]))
|
|
||||||
line_item_of = JournalEntryLineItemData(
|
|
||||||
Accounts.PAYABLE, "Computer", "800",
|
|
||||||
original_line_item=line_item_or)
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
35, [JournalEntryCurrencyData(
|
|
||||||
"USD", [line_item_of], [JournalEntryLineItemData(
|
|
||||||
Accounts.BANK, "Computer", "800")])]))
|
|
||||||
line_item_of = JournalEntryLineItemData(
|
|
||||||
Accounts.PAYABLE, "Computer", "400",
|
|
||||||
original_line_item=line_item_or)
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
10, [JournalEntryCurrencyData(
|
|
||||||
"USD", [line_item_of], [JournalEntryLineItemData(
|
|
||||||
Accounts.CASH, "Computer", "400")])]))
|
|
||||||
|
|
||||||
# Full offset and unmatched in TWD
|
|
||||||
description = "演講費—母校"
|
|
||||||
line_item_or = JournalEntryLineItemData(
|
|
||||||
Accounts.RECEIVABLE, description, "3000")
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
45, [JournalEntryCurrencyData(
|
|
||||||
"TWD", [line_item_or], [JournalEntryLineItemData(
|
|
||||||
Accounts.SERVICE, description, "3000")])]))
|
|
||||||
line_item_of = JournalEntryLineItemData(
|
|
||||||
Accounts.RECEIVABLE, description, "3000",
|
|
||||||
original_line_item=line_item_or)
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
6, [JournalEntryCurrencyData(
|
|
||||||
"TWD", [JournalEntryLineItemData(
|
|
||||||
Accounts.BANK, description, "3000")],
|
|
||||||
[line_item_of])]))
|
|
||||||
self.__add_journal_entry(
|
|
||||||
25, "TWD", "3000",
|
|
||||||
Accounts.BANK, description, Accounts.SERVICE, description)
|
|
||||||
|
|
||||||
# Partial offset in TWD
|
|
||||||
line_item_or = JournalEntryLineItemData(
|
|
||||||
Accounts.PAYABLE, "手機", "30000")
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
55, [JournalEntryCurrencyData(
|
|
||||||
"TWD", [JournalEntryLineItemData(
|
|
||||||
Accounts.MACHINERY, "手機", "30000")],
|
|
||||||
[line_item_or])]))
|
|
||||||
line_item_of = JournalEntryLineItemData(
|
|
||||||
Accounts.PAYABLE, "手機", "16000",
|
|
||||||
original_line_item=line_item_or)
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
27, [JournalEntryCurrencyData(
|
|
||||||
"TWD", [line_item_of], [JournalEntryLineItemData(
|
|
||||||
Accounts.BANK, "手機", "16000")])]))
|
|
||||||
line_item_of = JournalEntryLineItemData(
|
|
||||||
Accounts.PAYABLE, "手機", "6000",
|
|
||||||
original_line_item=line_item_or)
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
8, [JournalEntryCurrencyData(
|
|
||||||
"TWD", [line_item_of], [JournalEntryLineItemData(
|
|
||||||
Accounts.CASH, "手機", "6000")])]))
|
|
||||||
|
|
||||||
def __add_meals(self) -> None:
|
|
||||||
"""Adds the meal data.
|
|
||||||
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
days = 60
|
|
||||||
while days >= 0:
|
|
||||||
# Meals in USD
|
|
||||||
if days % 4 == 2:
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "USD", "2.9",
|
|
||||||
Accounts.MEAL, "Lunch—Coffee", Accounts.CASH, None)
|
|
||||||
else:
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "USD", "3.9",
|
|
||||||
Accounts.MEAL, "Lunch—Coffee", Accounts.CASH, None)
|
|
||||||
|
|
||||||
if days % 15 == 3:
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "USD", "5.45",
|
|
||||||
Accounts.MEAL, "Dinner—Pizza",
|
|
||||||
Accounts.PAYABLE, "Dinner—Pizza")
|
|
||||||
else:
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "USD", "5.9",
|
|
||||||
Accounts.MEAL, "Dinner—Pasta", Accounts.CASH, None)
|
|
||||||
|
|
||||||
# Meals in TWD
|
|
||||||
if days % 5 == 3:
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "TWD", "125",
|
|
||||||
Accounts.MEAL, "午餐—鄰家咖啡", Accounts.CASH, None)
|
|
||||||
else:
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "TWD", "80",
|
|
||||||
Accounts.MEAL, "午餐—便當", Accounts.CASH, None)
|
|
||||||
|
|
||||||
if days % 15 == 3:
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "TWD", "320",
|
|
||||||
Accounts.MEAL, "晚餐—牛排", Accounts.PAYABLE, "晚餐—牛排")
|
|
||||||
else:
|
|
||||||
self.__add_journal_entry(
|
|
||||||
days, "TWD", "100",
|
|
||||||
Accounts.MEAL, "晚餐—自助餐", Accounts.CASH, None)
|
|
||||||
|
|
||||||
days = days - 1
|
|
||||||
|
|
||||||
def __add_journal_entry(
|
|
||||||
self, days: int, currency: str, amount: str,
|
|
||||||
debit_account: str, debit_description: str | None,
|
|
||||||
credit_account: str, credit_description: str | None) -> None:
|
|
||||||
"""Adds a simple journal entry.
|
|
||||||
|
|
||||||
:param days: The number of days before today.
|
|
||||||
:param currency: The currency code.
|
|
||||||
:param amount: The amount.
|
|
||||||
:param debit_account: The debit account code.
|
|
||||||
:param debit_description: The debit description.
|
|
||||||
:param credit_account: The credit account code.
|
|
||||||
:param credit_description: The credit description.
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
self._add_journal_entry(JournalEntryData(
|
|
||||||
days,
|
|
||||||
[JournalEntryCurrencyData(
|
|
||||||
currency,
|
|
||||||
[JournalEntryLineItemData(
|
|
||||||
debit_account, debit_description, amount)],
|
|
||||||
[JournalEntryLineItemData(
|
|
||||||
credit_account, credit_description, amount)])]))
|
|
||||||
|
|
||||||
def write_journal_entries(self, file: Path) -> None:
|
|
||||||
"""Writes the journal entries to the CSV file.
|
|
||||||
|
|
||||||
:param file: The CSV file.
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
today: date = date.today()
|
|
||||||
|
|
||||||
def filter_data(data: dict[str, t.Any]) -> dict[str, t.Any]:
|
|
||||||
"""Filters the journal entry data for JSON encoding.
|
|
||||||
|
|
||||||
:param data: The journal entry data.
|
|
||||||
:return: The journal entry data for JSON encoding.
|
|
||||||
"""
|
|
||||||
data = data.copy()
|
|
||||||
data["date"] = (today - data["date"]).days
|
|
||||||
del data["created_by_id"]
|
|
||||||
del data["updated_by_id"]
|
|
||||||
return data
|
|
||||||
|
|
||||||
with open(file, "wt") as fp:
|
|
||||||
writer: csv.DictWriter = csv.DictWriter(
|
|
||||||
fp, fieldnames=["id", "date", "no", "note"])
|
|
||||||
writer.writeheader()
|
|
||||||
writer.writerows([filter_data(x) for x in self._journal_entries])
|
|
||||||
|
|
||||||
def write_line_items(self, file: Path) -> None:
|
|
||||||
"""Writes the journal entries to the CSV file.
|
|
||||||
|
|
||||||
:param file: The CSV file.
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
from accounting import db
|
|
||||||
from accounting.models import Account
|
|
||||||
|
|
||||||
def filter_data(data: dict[str, t.Any]) -> dict[str, t.Any]:
|
|
||||||
"""Filters the journal entry line item data for JSON encoding.
|
|
||||||
|
|
||||||
:param data: The journal entry line item data.
|
|
||||||
:return: The journal entry line item data for JSON encoding.
|
|
||||||
"""
|
|
||||||
data = data.copy()
|
|
||||||
with self._app.app_context():
|
|
||||||
data["account_id"] \
|
|
||||||
= db.session.get(Account, data["account_id"]).code
|
|
||||||
if "original_line_item_id" not in data:
|
|
||||||
data["original_line_item_id"] = None
|
|
||||||
data["is_debit"] = "1" if data["is_debit"] else ""
|
|
||||||
return data
|
|
||||||
|
|
||||||
with open(file, "wt") as fp:
|
|
||||||
writer: csv.DictWriter = csv.DictWriter(
|
|
||||||
fp, fieldnames=["id", "journal_entry_id",
|
|
||||||
"original_line_item_id", "is_debit", "no",
|
|
||||||
"account_id", "currency_code", "description",
|
|
||||||
"amount"])
|
|
||||||
writer.writeheader()
|
|
||||||
writer.writerows([filter_data(x) for x in self._line_items])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@ -1,762 +0,0 @@
|
|||||||
id,date,no,note
|
|
||||||
920595794,1825,1,
|
|
||||||
167719688,1824,1,
|
|
||||||
924719319,1811,1,
|
|
||||||
323507578,1810,1,
|
|
||||||
262621044,1797,1,
|
|
||||||
653817937,1796,1,
|
|
||||||
816967804,1783,1,
|
|
||||||
856488549,1782,1,
|
|
||||||
974220139,1769,1,
|
|
||||||
564198001,1768,1,
|
|
||||||
534029375,1755,1,
|
|
||||||
409536685,1754,1,
|
|
||||||
696925565,1741,1,
|
|
||||||
311111882,1740,1,
|
|
||||||
330777832,1727,1,
|
|
||||||
113025054,1726,1,
|
|
||||||
487817808,1713,1,
|
|
||||||
241456952,1712,1,
|
|
||||||
364065422,1699,1,
|
|
||||||
167733813,1698,1,
|
|
||||||
866421596,1685,1,
|
|
||||||
763529473,1684,1,
|
|
||||||
833713134,1671,1,
|
|
||||||
168537646,1670,1,
|
|
||||||
680934663,1657,1,
|
|
||||||
608321515,1656,1,
|
|
||||||
590554378,1643,1,
|
|
||||||
822636649,1642,1,
|
|
||||||
502438349,1629,1,
|
|
||||||
916479257,1628,1,
|
|
||||||
759312362,1615,1,
|
|
||||||
777388379,1614,1,
|
|
||||||
128533828,1601,1,
|
|
||||||
880256320,1600,1,
|
|
||||||
300937285,1587,1,
|
|
||||||
663824372,1586,1,
|
|
||||||
328847075,1573,1,
|
|
||||||
340162514,1572,1,
|
|
||||||
594742129,1559,1,
|
|
||||||
127751868,1558,1,
|
|
||||||
975252333,1545,1,
|
|
||||||
164653789,1544,1,
|
|
||||||
913321677,1531,1,
|
|
||||||
644327573,1530,1,
|
|
||||||
646511092,1517,1,
|
|
||||||
542576751,1516,1,
|
|
||||||
721300970,1503,1,
|
|
||||||
119119879,1502,1,
|
|
||||||
778655945,1489,1,
|
|
||||||
954739598,1488,1,
|
|
||||||
160677124,1475,1,
|
|
||||||
650444891,1474,1,
|
|
||||||
275286747,1461,1,
|
|
||||||
586298065,1460,1,
|
|
||||||
483231167,1447,1,
|
|
||||||
677238826,1446,1,
|
|
||||||
987666214,1433,1,
|
|
||||||
615122154,1432,1,
|
|
||||||
192638198,1419,1,
|
|
||||||
522150224,1418,1,
|
|
||||||
157970376,1405,1,
|
|
||||||
672859325,1404,1,
|
|
||||||
766319138,1391,1,
|
|
||||||
202527913,1390,1,
|
|
||||||
544153063,1377,1,
|
|
||||||
776427441,1376,1,
|
|
||||||
201385617,1363,1,
|
|
||||||
836692365,1362,1,
|
|
||||||
635455165,1349,1,
|
|
||||||
601199000,1348,1,
|
|
||||||
223698225,1335,1,
|
|
||||||
292479913,1334,1,
|
|
||||||
151098400,1321,1,
|
|
||||||
239171360,1320,1,
|
|
||||||
897871634,1307,1,
|
|
||||||
715964039,1306,1,
|
|
||||||
721522332,1293,1,
|
|
||||||
862264784,1292,1,
|
|
||||||
839281301,1279,1,
|
|
||||||
623085076,1278,1,
|
|
||||||
359772314,1265,1,
|
|
||||||
789931149,1264,1,
|
|
||||||
294683943,1251,1,
|
|
||||||
719606226,1250,1,
|
|
||||||
636698875,1237,1,
|
|
||||||
300098192,1236,1,
|
|
||||||
158995071,1223,1,
|
|
||||||
119944658,1222,1,
|
|
||||||
609243413,1209,1,
|
|
||||||
937075952,1208,1,
|
|
||||||
982505748,1195,1,
|
|
||||||
597742281,1194,1,
|
|
||||||
432728297,1181,1,
|
|
||||||
294593180,1180,1,
|
|
||||||
366178386,1167,1,
|
|
||||||
363145239,1166,1,
|
|
||||||
404215195,1153,1,
|
|
||||||
734768107,1152,1,
|
|
||||||
718837714,1139,1,
|
|
||||||
676223847,1138,1,
|
|
||||||
527324347,1125,1,
|
|
||||||
127545563,1124,1,
|
|
||||||
285970855,1111,1,
|
|
||||||
494617980,1110,1,
|
|
||||||
618075266,1097,1,
|
|
||||||
886514998,1096,1,
|
|
||||||
669449408,1083,1,
|
|
||||||
905732390,1082,1,
|
|
||||||
120868897,1069,1,
|
|
||||||
968895440,1068,1,
|
|
||||||
594126056,1055,1,
|
|
||||||
983321611,1054,1,
|
|
||||||
217258196,1041,1,
|
|
||||||
748044362,1040,1,
|
|
||||||
550938659,1027,1,
|
|
||||||
499350285,1026,1,
|
|
||||||
474800323,1013,1,
|
|
||||||
941375983,1012,1,
|
|
||||||
672583721,999,1,
|
|
||||||
235397028,998,1,
|
|
||||||
471789584,985,1,
|
|
||||||
767845926,984,1,
|
|
||||||
196974640,971,1,
|
|
||||||
407699876,970,1,
|
|
||||||
584761419,957,1,
|
|
||||||
645818558,956,1,
|
|
||||||
844178241,943,1,
|
|
||||||
963904656,942,1,
|
|
||||||
329751044,929,1,
|
|
||||||
616057487,928,1,
|
|
||||||
677792701,915,1,
|
|
||||||
229571999,914,1,
|
|
||||||
770629438,901,1,
|
|
||||||
974300311,900,1,
|
|
||||||
622448143,887,1,
|
|
||||||
775027570,886,1,
|
|
||||||
977691200,873,1,
|
|
||||||
315594452,872,1,
|
|
||||||
758899307,859,1,
|
|
||||||
978022319,858,1,
|
|
||||||
228533193,845,1,
|
|
||||||
557094954,844,1,
|
|
||||||
934274659,831,1,
|
|
||||||
767710804,830,1,
|
|
||||||
991004642,817,1,
|
|
||||||
800201824,816,1,
|
|
||||||
863909652,803,1,
|
|
||||||
425568442,802,1,
|
|
||||||
410006227,789,1,
|
|
||||||
423235300,788,1,
|
|
||||||
671001264,775,1,
|
|
||||||
393949116,774,1,
|
|
||||||
647460045,761,1,
|
|
||||||
345027852,760,1,
|
|
||||||
389485218,747,1,
|
|
||||||
562198875,746,1,
|
|
||||||
721297709,733,1,
|
|
||||||
684907870,732,1,
|
|
||||||
490439441,719,1,
|
|
||||||
749713268,718,1,
|
|
||||||
381786692,705,1,
|
|
||||||
894638770,704,1,
|
|
||||||
358593852,691,1,
|
|
||||||
215132977,690,1,
|
|
||||||
308109683,677,1,
|
|
||||||
957835487,676,1,
|
|
||||||
806684705,663,1,
|
|
||||||
426159370,662,1,
|
|
||||||
121182405,649,1,
|
|
||||||
957960762,648,1,
|
|
||||||
141944057,635,1,
|
|
||||||
241049223,634,1,
|
|
||||||
348998240,621,1,
|
|
||||||
320606991,620,1,
|
|
||||||
686655353,607,1,
|
|
||||||
961773909,606,1,
|
|
||||||
404465691,593,1,
|
|
||||||
611214094,592,1,
|
|
||||||
560924807,579,1,
|
|
||||||
580060734,578,1,
|
|
||||||
156733729,565,1,
|
|
||||||
621737486,564,1,
|
|
||||||
416009158,551,1,
|
|
||||||
385647761,550,1,
|
|
||||||
949563508,537,1,
|
|
||||||
275121057,536,1,
|
|
||||||
812541441,523,1,
|
|
||||||
852720162,522,1,
|
|
||||||
924747979,509,1,
|
|
||||||
985292129,508,1,
|
|
||||||
923148238,495,1,
|
|
||||||
362041242,494,1,
|
|
||||||
811169848,481,1,
|
|
||||||
871493096,480,1,
|
|
||||||
213966403,467,1,
|
|
||||||
943205571,466,1,
|
|
||||||
866531476,453,1,
|
|
||||||
845270003,452,1,
|
|
||||||
968566807,439,1,
|
|
||||||
691976185,438,1,
|
|
||||||
785453444,425,1,
|
|
||||||
282506457,424,1,
|
|
||||||
617017554,411,1,
|
|
||||||
780203660,410,1,
|
|
||||||
571247306,397,1,
|
|
||||||
920518434,396,1,
|
|
||||||
414175655,383,1,
|
|
||||||
549085154,382,1,
|
|
||||||
838137695,369,1,
|
|
||||||
518589128,368,1,
|
|
||||||
210878113,355,1,
|
|
||||||
946224829,354,1,
|
|
||||||
157303099,341,1,
|
|
||||||
887584486,340,1,
|
|
||||||
544035334,327,1,
|
|
||||||
417741323,326,1,
|
|
||||||
424475535,313,1,
|
|
||||||
636455373,312,1,
|
|
||||||
946803193,299,1,
|
|
||||||
623495077,298,1,
|
|
||||||
704429996,285,1,
|
|
||||||
129822874,284,1,
|
|
||||||
274101370,271,1,
|
|
||||||
169507838,270,1,
|
|
||||||
227085275,257,1,
|
|
||||||
809230420,256,1,
|
|
||||||
489045353,243,1,
|
|
||||||
850581440,242,1,
|
|
||||||
415297271,229,1,
|
|
||||||
423539815,228,1,
|
|
||||||
653210790,215,1,
|
|
||||||
737777742,214,1,
|
|
||||||
788141156,201,1,
|
|
||||||
975326987,200,1,
|
|
||||||
496699465,187,1,
|
|
||||||
117816559,186,1,
|
|
||||||
158496631,173,1,
|
|
||||||
564646901,172,1,
|
|
||||||
795983274,159,1,
|
|
||||||
274728220,158,1,
|
|
||||||
581586933,145,1,
|
|
||||||
649099645,144,1,
|
|
||||||
243513393,131,1,
|
|
||||||
402999178,130,1,
|
|
||||||
823867439,117,1,
|
|
||||||
271370438,116,1,
|
|
||||||
146329215,103,1,
|
|
||||||
707167506,102,1,
|
|
||||||
490971858,89,1,
|
|
||||||
803145319,88,1,
|
|
||||||
141610606,75,1,
|
|
||||||
573278167,74,1,
|
|
||||||
926268699,61,1,
|
|
||||||
259559611,60,1,
|
|
||||||
653092670,47,1,
|
|
||||||
173180273,46,1,
|
|
||||||
399973243,33,1,
|
|
||||||
950572866,32,1,
|
|
||||||
431937430,19,1,
|
|
||||||
889697632,18,1,
|
|
||||||
688217608,5,1,
|
|
||||||
205619160,4,1,
|
|
||||||
441864102,1807,1,
|
|
||||||
912340574,1776,1,
|
|
||||||
209734890,1746,1,
|
|
||||||
243847232,1715,1,
|
|
||||||
545178022,1684,2,
|
|
||||||
757644914,1654,1,
|
|
||||||
709824777,1623,1,
|
|
||||||
702866006,1593,1,
|
|
||||||
677273983,1562,1,
|
|
||||||
241001022,1531,2,
|
|
||||||
578066267,1503,2,
|
|
||||||
598819130,1472,1,
|
|
||||||
815746861,1442,1,
|
|
||||||
893881104,1411,1,
|
|
||||||
874097643,1381,1,
|
|
||||||
928946469,1350,1,
|
|
||||||
739916751,1319,1,
|
|
||||||
628915518,1289,1,
|
|
||||||
158355796,1258,1,
|
|
||||||
977635103,1228,1,
|
|
||||||
403376322,1197,1,
|
|
||||||
401612265,1166,2,
|
|
||||||
533623827,1137,1,
|
|
||||||
670798503,1106,1,
|
|
||||||
270312680,1076,1,
|
|
||||||
184537410,1045,1,
|
|
||||||
160595264,1015,1,
|
|
||||||
599914415,984,2,
|
|
||||||
969597247,953,1,
|
|
||||||
836450793,923,1,
|
|
||||||
556074668,892,1,
|
|
||||||
774322200,862,1,
|
|
||||||
383523937,831,2,
|
|
||||||
823934445,800,1,
|
|
||||||
119545102,772,1,
|
|
||||||
679121510,741,1,
|
|
||||||
509206305,711,1,
|
|
||||||
588269753,680,1,
|
|
||||||
364261289,650,1,
|
|
||||||
297729584,619,1,
|
|
||||||
924371653,588,1,
|
|
||||||
512378982,558,1,
|
|
||||||
182081687,527,1,
|
|
||||||
844596552,497,1,
|
|
||||||
242512891,466,2,
|
|
||||||
708522969,435,1,
|
|
||||||
993303180,407,1,
|
|
||||||
315847857,376,1,
|
|
||||||
299835785,346,1,
|
|
||||||
543632811,315,1,
|
|
||||||
102186083,285,2,
|
|
||||||
160769407,254,1,
|
|
||||||
655405209,223,1,
|
|
||||||
833962437,193,1,
|
|
||||||
167207427,162,1,
|
|
||||||
943978137,132,1,
|
|
||||||
302101265,101,1,
|
|
||||||
278323943,70,1,
|
|
||||||
175211372,42,1,
|
|
||||||
608345857,11,1,
|
|
||||||
514771804,1833,1,
|
|
||||||
593369716,1832,1,
|
|
||||||
705767689,1828,1,
|
|
||||||
593372990,1803,1,
|
|
||||||
184537754,1802,1,
|
|
||||||
995622116,1798,1,
|
|
||||||
749424364,1772,1,
|
|
||||||
121488238,1771,1,
|
|
||||||
640079073,1767,1,
|
|
||||||
824601810,1742,1,
|
|
||||||
542282982,1741,2,
|
|
||||||
994649755,1737,1,
|
|
||||||
983539118,1711,1,
|
|
||||||
367416911,1710,1,
|
|
||||||
532597030,1706,1,
|
|
||||||
572446528,1680,1,
|
|
||||||
340236287,1679,1,
|
|
||||||
759591805,1675,1,
|
|
||||||
886317404,1650,1,
|
|
||||||
351767548,1649,1,
|
|
||||||
646170267,1645,1,
|
|
||||||
343093301,1619,1,
|
|
||||||
705817069,1618,1,
|
|
||||||
912206806,1614,2,
|
|
||||||
244039693,1589,1,
|
|
||||||
623877530,1588,1,
|
|
||||||
193626244,1584,1,
|
|
||||||
864752440,1558,2,
|
|
||||||
698269931,1557,1,
|
|
||||||
723712694,1553,1,
|
|
||||||
574930230,1527,1,
|
|
||||||
544819126,1526,1,
|
|
||||||
384766666,1522,1,
|
|
||||||
651851628,1499,1,
|
|
||||||
952119252,1498,1,
|
|
||||||
674668054,1494,1,
|
|
||||||
547522619,1468,1,
|
|
||||||
173186830,1467,1,
|
|
||||||
865012414,1463,1,
|
|
||||||
206856704,1438,1,
|
|
||||||
801403464,1437,1,
|
|
||||||
111325157,1433,2,
|
|
||||||
262372074,1407,1,
|
|
||||||
784935817,1406,1,
|
|
||||||
340522160,1402,1,
|
|
||||||
835792937,1377,2,
|
|
||||||
638617129,1376,2,
|
|
||||||
730208680,1372,1,
|
|
||||||
156194429,1346,1,
|
|
||||||
699671683,1345,1,
|
|
||||||
497947013,1341,1,
|
|
||||||
786969266,1315,1,
|
|
||||||
965456430,1314,1,
|
|
||||||
741480080,1310,1,
|
|
||||||
996594000,1285,1,
|
|
||||||
759434327,1284,1,
|
|
||||||
248929835,1280,1,
|
|
||||||
100188306,1254,1,
|
|
||||||
397143707,1253,1,
|
|
||||||
997071662,1249,1,
|
|
||||||
799963514,1224,1,
|
|
||||||
586462685,1223,2,
|
|
||||||
840211917,1219,1,
|
|
||||||
280297680,1193,1,
|
|
||||||
622664327,1192,1,
|
|
||||||
337423599,1188,1,
|
|
||||||
744409889,1162,1,
|
|
||||||
324290549,1161,1,
|
|
||||||
399978290,1157,1,
|
|
||||||
976614162,1133,1,
|
|
||||||
733411608,1132,1,
|
|
||||||
779502252,1128,1,
|
|
||||||
694642910,1102,1,
|
|
||||||
589967992,1101,1,
|
|
||||||
812154532,1097,2,
|
|
||||||
446336381,1072,1,
|
|
||||||
997465638,1071,1,
|
|
||||||
695463728,1067,1,
|
|
||||||
437461833,1041,2,
|
|
||||||
363848061,1040,2,
|
|
||||||
832431988,1036,1,
|
|
||||||
150221940,1011,1,
|
|
||||||
928316021,1010,1,
|
|
||||||
614264138,1006,1,
|
|
||||||
844037652,980,1,
|
|
||||||
847052057,979,1,
|
|
||||||
244380507,975,1,
|
|
||||||
664173677,949,1,
|
|
||||||
910360755,948,1,
|
|
||||||
449187964,944,1,
|
|
||||||
655278726,919,1,
|
|
||||||
749024286,918,1,
|
|
||||||
588856997,914,2,
|
|
||||||
698675186,888,1,
|
|
||||||
235049763,887,2,
|
|
||||||
190317946,883,1,
|
|
||||||
394258185,858,2,
|
|
||||||
683058769,857,1,
|
|
||||||
569480987,853,1,
|
|
||||||
794010803,827,1,
|
|
||||||
517616540,826,1,
|
|
||||||
126618363,822,1,
|
|
||||||
165804887,796,1,
|
|
||||||
712868700,795,1,
|
|
||||||
448615093,791,1,
|
|
||||||
292392546,768,1,
|
|
||||||
176399757,767,1,
|
|
||||||
941309949,763,1,
|
|
||||||
917170432,737,1,
|
|
||||||
393779218,736,1,
|
|
||||||
229114748,732,2,
|
|
||||||
796830345,707,1,
|
|
||||||
361375142,706,1,
|
|
||||||
636984952,702,1,
|
|
||||||
183871802,676,2,
|
|
||||||
726573750,675,1,
|
|
||||||
104003572,671,1,
|
|
||||||
207516690,646,1,
|
|
||||||
886960586,645,1,
|
|
||||||
796556194,641,1,
|
|
||||||
280072790,615,1,
|
|
||||||
376646507,614,1,
|
|
||||||
670013900,610,1,
|
|
||||||
710911370,584,1,
|
|
||||||
692062709,583,1,
|
|
||||||
945120601,579,2,
|
|
||||||
238099067,554,1,
|
|
||||||
141946232,553,1,
|
|
||||||
363924657,549,1,
|
|
||||||
200423540,523,2,
|
|
||||||
450386730,522,2,
|
|
||||||
501201103,518,1,
|
|
||||||
722592147,493,1,
|
|
||||||
111175359,492,1,
|
|
||||||
695275339,488,1,
|
|
||||||
770948173,462,1,
|
|
||||||
125366752,461,1,
|
|
||||||
495203796,457,1,
|
|
||||||
436996037,431,1,
|
|
||||||
537753065,430,1,
|
|
||||||
870522427,426,1,
|
|
||||||
150461691,403,1,
|
|
||||||
213932656,402,1,
|
|
||||||
141488648,398,1,
|
|
||||||
658991952,372,1,
|
|
||||||
787118332,371,1,
|
|
||||||
453823736,367,1,
|
|
||||||
164470486,342,1,
|
|
||||||
541106783,341,2,
|
|
||||||
922957464,337,1,
|
|
||||||
311051490,311,1,
|
|
||||||
655629665,310,1,
|
|
||||||
547633846,306,1,
|
|
||||||
466122789,281,1,
|
|
||||||
748946528,280,1,
|
|
||||||
432001491,276,1,
|
|
||||||
229423752,250,1,
|
|
||||||
481974272,249,1,
|
|
||||||
117595916,245,1,
|
|
||||||
964413002,219,1,
|
|
||||||
107966394,218,1,
|
|
||||||
351737718,214,2,
|
|
||||||
101282939,189,1,
|
|
||||||
400238568,188,1,
|
|
||||||
179085420,184,1,
|
|
||||||
159931628,158,2,
|
|
||||||
115738557,157,1,
|
|
||||||
291513790,153,1,
|
|
||||||
713892472,128,1,
|
|
||||||
620731758,127,1,
|
|
||||||
856667031,123,1,
|
|
||||||
927266285,97,1,
|
|
||||||
978689593,96,1,
|
|
||||||
381169193,92,1,
|
|
||||||
599832757,66,1,
|
|
||||||
142513296,65,1,
|
|
||||||
921491954,61,2,
|
|
||||||
839977040,38,1,
|
|
||||||
805264458,37,1,
|
|
||||||
423929180,33,2,
|
|
||||||
228098407,7,1,
|
|
||||||
911305213,6,1,
|
|
||||||
116464373,2,1,
|
|
||||||
108355497,40,1,
|
|
||||||
218698859,5,2,
|
|
||||||
638428066,30,1,
|
|
||||||
116571429,60,2,
|
|
||||||
824441170,35,1,
|
|
||||||
934539456,10,1,
|
|
||||||
271350601,45,1,
|
|
||||||
870787195,6,2,
|
|
||||||
284648942,25,1,
|
|
||||||
438677999,55,1,
|
|
||||||
501435816,27,1,
|
|
||||||
180500081,8,1,
|
|
||||||
468159641,60,3,
|
|
||||||
722956493,60,4,
|
|
||||||
560303779,60,5,
|
|
||||||
238549424,60,6,
|
|
||||||
768396487,59,1,
|
|
||||||
517470102,59,2,
|
|
||||||
965190270,59,3,
|
|
||||||
215903999,59,4,
|
|
||||||
478029651,58,1,
|
|
||||||
874038847,58,2,
|
|
||||||
849913651,58,3,
|
|
||||||
761054191,58,4,
|
|
||||||
400796832,57,1,
|
|
||||||
970704514,57,2,
|
|
||||||
556356224,57,3,
|
|
||||||
741253588,57,4,
|
|
||||||
355733835,56,1,
|
|
||||||
323736502,56,2,
|
|
||||||
451515758,56,3,
|
|
||||||
225990920,56,4,
|
|
||||||
992232778,55,2,
|
|
||||||
647459911,55,3,
|
|
||||||
789334321,55,4,
|
|
||||||
717150023,55,5,
|
|
||||||
845627271,54,1,
|
|
||||||
487627248,54,2,
|
|
||||||
839234085,54,3,
|
|
||||||
113633678,54,4,
|
|
||||||
971665356,53,1,
|
|
||||||
934382703,53,2,
|
|
||||||
760076948,53,3,
|
|
||||||
809509517,53,4,
|
|
||||||
596847052,52,1,
|
|
||||||
745457603,52,2,
|
|
||||||
503874758,52,3,
|
|
||||||
646819576,52,4,
|
|
||||||
836672490,51,1,
|
|
||||||
452049971,51,2,
|
|
||||||
147242491,51,3,
|
|
||||||
906978237,51,4,
|
|
||||||
329394419,50,1,
|
|
||||||
797892803,50,2,
|
|
||||||
613999829,50,3,
|
|
||||||
696139462,50,4,
|
|
||||||
887080294,49,1,
|
|
||||||
388730039,49,2,
|
|
||||||
890037830,49,3,
|
|
||||||
581863261,49,4,
|
|
||||||
579390300,48,1,
|
|
||||||
333715152,48,2,
|
|
||||||
523632671,48,3,
|
|
||||||
366230483,48,4,
|
|
||||||
857547265,47,2,
|
|
||||||
109733956,47,3,
|
|
||||||
114838736,47,4,
|
|
||||||
913691134,47,5,
|
|
||||||
531295049,46,2,
|
|
||||||
421868682,46,3,
|
|
||||||
427214791,46,4,
|
|
||||||
221316724,46,5,
|
|
||||||
191024248,45,2,
|
|
||||||
213602801,45,3,
|
|
||||||
614533920,45,4,
|
|
||||||
493931718,45,5,
|
|
||||||
238314782,44,1,
|
|
||||||
710782153,44,2,
|
|
||||||
462494012,44,3,
|
|
||||||
358897897,44,4,
|
|
||||||
210206997,43,1,
|
|
||||||
486781355,43,2,
|
|
||||||
872028540,43,3,
|
|
||||||
972765259,43,4,
|
|
||||||
265930733,42,2,
|
|
||||||
671569090,42,3,
|
|
||||||
579480125,42,4,
|
|
||||||
463751608,42,5,
|
|
||||||
709403395,41,1,
|
|
||||||
173246446,41,2,
|
|
||||||
663555479,41,3,
|
|
||||||
999954005,41,4,
|
|
||||||
608615196,40,2,
|
|
||||||
838019513,40,3,
|
|
||||||
592119748,40,4,
|
|
||||||
126852606,40,5,
|
|
||||||
113264316,39,1,
|
|
||||||
988673782,39,2,
|
|
||||||
331146869,39,3,
|
|
||||||
628774637,39,4,
|
|
||||||
667988387,38,2,
|
|
||||||
853384636,38,3,
|
|
||||||
925053987,38,4,
|
|
||||||
245176667,38,5,
|
|
||||||
126299195,37,2,
|
|
||||||
736982561,37,3,
|
|
||||||
722196792,37,4,
|
|
||||||
591364013,37,5,
|
|
||||||
724565500,36,1,
|
|
||||||
259676294,36,2,
|
|
||||||
907087078,36,3,
|
|
||||||
457074841,36,4,
|
|
||||||
276057268,35,2,
|
|
||||||
792870181,35,3,
|
|
||||||
291259938,35,4,
|
|
||||||
696660280,35,5,
|
|
||||||
375748406,34,1,
|
|
||||||
958051707,34,2,
|
|
||||||
777603420,34,3,
|
|
||||||
235964104,34,4,
|
|
||||||
249855189,33,3,
|
|
||||||
264355079,33,4,
|
|
||||||
565768229,33,5,
|
|
||||||
940942156,33,6,
|
|
||||||
334860946,32,2,
|
|
||||||
974029840,32,3,
|
|
||||||
885953975,32,4,
|
|
||||||
121142827,32,5,
|
|
||||||
439417290,31,1,
|
|
||||||
915459717,31,2,
|
|
||||||
532456051,31,3,
|
|
||||||
961300250,31,4,
|
|
||||||
106171276,30,2,
|
|
||||||
341023837,30,3,
|
|
||||||
655719662,30,4,
|
|
||||||
239701820,30,5,
|
|
||||||
669321248,29,1,
|
|
||||||
380569098,29,2,
|
|
||||||
405140021,29,3,
|
|
||||||
359640438,29,4,
|
|
||||||
509795327,28,1,
|
|
||||||
487999378,28,2,
|
|
||||||
828335342,28,3,
|
|
||||||
418476186,28,4,
|
|
||||||
372067537,27,2,
|
|
||||||
416154021,27,3,
|
|
||||||
504319878,27,4,
|
|
||||||
148303022,27,5,
|
|
||||||
755070679,26,1,
|
|
||||||
718789501,26,2,
|
|
||||||
722521549,26,3,
|
|
||||||
510505515,26,4,
|
|
||||||
189764314,25,2,
|
|
||||||
283953036,25,3,
|
|
||||||
941354935,25,4,
|
|
||||||
722404083,25,5,
|
|
||||||
844973537,24,1,
|
|
||||||
936745933,24,2,
|
|
||||||
659022926,24,3,
|
|
||||||
185376101,24,4,
|
|
||||||
725191286,23,1,
|
|
||||||
321637118,23,2,
|
|
||||||
788778448,23,3,
|
|
||||||
585477956,23,4,
|
|
||||||
605054808,22,1,
|
|
||||||
780188639,22,2,
|
|
||||||
780018244,22,3,
|
|
||||||
857065036,22,4,
|
|
||||||
199089854,21,1,
|
|
||||||
348192086,21,2,
|
|
||||||
289518063,21,3,
|
|
||||||
831333771,21,4,
|
|
||||||
510792845,20,1,
|
|
||||||
234156394,20,2,
|
|
||||||
187419584,20,3,
|
|
||||||
935322395,20,4,
|
|
||||||
348520531,19,2,
|
|
||||||
417969553,19,3,
|
|
||||||
692795304,19,4,
|
|
||||||
213131066,19,5,
|
|
||||||
558144531,18,2,
|
|
||||||
680562985,18,3,
|
|
||||||
370027571,18,4,
|
|
||||||
420457647,18,5,
|
|
||||||
177953996,17,1,
|
|
||||||
211565294,17,2,
|
|
||||||
594340749,17,3,
|
|
||||||
543836150,17,4,
|
|
||||||
520536761,16,1,
|
|
||||||
437394817,16,2,
|
|
||||||
606478888,16,3,
|
|
||||||
507521652,16,4,
|
|
||||||
208657583,15,1,
|
|
||||||
312837142,15,2,
|
|
||||||
236775257,15,3,
|
|
||||||
292716353,15,4,
|
|
||||||
489753786,14,1,
|
|
||||||
932441865,14,2,
|
|
||||||
335340858,14,3,
|
|
||||||
203222754,14,4,
|
|
||||||
735247201,13,1,
|
|
||||||
508845275,13,2,
|
|
||||||
902053604,13,3,
|
|
||||||
810554908,13,4,
|
|
||||||
451882915,12,1,
|
|
||||||
818503006,12,2,
|
|
||||||
760753816,12,3,
|
|
||||||
207784823,12,4,
|
|
||||||
658437082,11,2,
|
|
||||||
687949009,11,3,
|
|
||||||
775562720,11,4,
|
|
||||||
668017098,11,5,
|
|
||||||
434298402,10,2,
|
|
||||||
721116817,10,3,
|
|
||||||
597672211,10,4,
|
|
||||||
113135722,10,5,
|
|
||||||
732353307,9,1,
|
|
||||||
724984925,9,2,
|
|
||||||
952090946,9,3,
|
|
||||||
653723454,9,4,
|
|
||||||
520076751,8,2,
|
|
||||||
785925776,8,3,
|
|
||||||
286426950,8,4,
|
|
||||||
581197173,8,5,
|
|
||||||
778924243,7,2,
|
|
||||||
791851102,7,3,
|
|
||||||
370405076,7,4,
|
|
||||||
417757829,7,5,
|
|
||||||
635656498,6,3,
|
|
||||||
762929401,6,4,
|
|
||||||
856084599,6,5,
|
|
||||||
853906992,6,6,
|
|
||||||
642991787,5,3,
|
|
||||||
929081937,5,4,
|
|
||||||
298801582,5,5,
|
|
||||||
983831266,5,6,
|
|
||||||
153538209,4,2,
|
|
||||||
706440996,4,3,
|
|
||||||
557497187,4,4,
|
|
||||||
966432524,4,5,
|
|
||||||
322782940,3,1,
|
|
||||||
483498184,3,2,
|
|
||||||
741476687,3,3,
|
|
||||||
515764137,3,4,
|
|
||||||
213705166,2,2,
|
|
||||||
405160550,2,3,
|
|
||||||
473671974,2,4,
|
|
||||||
619813884,2,5,
|
|
||||||
197013244,1,1,
|
|
||||||
674079750,1,2,
|
|
||||||
631878930,1,3,
|
|
||||||
457552543,1,4,
|
|
||||||
477152158,0,1,
|
|
||||||
603928836,0,2,
|
|
||||||
225159020,0,3,
|
|
||||||
542040855,0,4,
|
|
|
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,18 @@ from . import db
|
|||||||
from .auth import User
|
from .auth import User
|
||||||
|
|
||||||
|
|
||||||
|
class Accounts:
|
||||||
|
"""The shortcuts to the common accounts."""
|
||||||
|
CASH: str = "1111-001"
|
||||||
|
BANK: str = "1113-001"
|
||||||
|
RECEIVABLE: str = "1141-001"
|
||||||
|
MACHINERY: str = "1441-001"
|
||||||
|
PAYABLE: str = "2141-001"
|
||||||
|
SERVICE: str = "4611-001"
|
||||||
|
RENT_EXPENSE: str = "6252-001"
|
||||||
|
MEAL: str = "6272-001"
|
||||||
|
|
||||||
|
|
||||||
class JournalEntryLineItemData:
|
class JournalEntryLineItemData:
|
||||||
"""The journal entry line item data."""
|
"""The journal entry line item data."""
|
||||||
|
|
||||||
|
@ -17,20 +17,16 @@
|
|||||||
"""The data reset for the Mia! Accounting demonstration website.
|
"""The data reset for the Mia! Accounting demonstration website.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import csv
|
|
||||||
import typing as t
|
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from decimal import Decimal
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import sqlalchemy as sa
|
|
||||||
from flask import Flask, Blueprint, url_for, flash, redirect, session, \
|
from flask import Flask, Blueprint, url_for, flash, redirect, session, \
|
||||||
render_template
|
render_template, current_app
|
||||||
from flask_babel import lazy_gettext
|
from flask_babel import lazy_gettext
|
||||||
|
|
||||||
from accounting.utils.cast import s
|
from accounting.utils.cast import s
|
||||||
from . import db
|
from . import db
|
||||||
from .auth import User, current_user
|
from .lib import Accounts, JournalEntryLineItemData, JournalEntryData, \
|
||||||
|
JournalEntryCurrencyData, BaseTestData
|
||||||
|
|
||||||
bp: Blueprint = Blueprint("reset", __name__, url_prefix="/")
|
bp: Blueprint = Blueprint("reset", __name__, url_prefix="/")
|
||||||
|
|
||||||
@ -51,7 +47,7 @@ def reset_sample() -> redirect:
|
|||||||
:return: Redirection to the accounting application.
|
:return: Redirection to the accounting application.
|
||||||
"""
|
"""
|
||||||
__reset_database()
|
__reset_database()
|
||||||
__populate_sample_data()
|
SampleData(current_app, "editor").populate()
|
||||||
flash(s(lazy_gettext(
|
flash(s(lazy_gettext(
|
||||||
"The sample data are emptied and reset successfully.")), "success")
|
"The sample data are emptied and reset successfully.")), "success")
|
||||||
return redirect(url_for("accounting-report.default"))
|
return redirect(url_for("accounting-report.default"))
|
||||||
@ -64,86 +60,10 @@ def clean_up() -> redirect:
|
|||||||
:return: Redirection to the accounting application.
|
:return: Redirection to the accounting application.
|
||||||
"""
|
"""
|
||||||
__reset_database()
|
__reset_database()
|
||||||
db.session.commit()
|
|
||||||
flash(s(lazy_gettext("The database is emptied successfully.")), "success")
|
flash(s(lazy_gettext("The database is emptied successfully.")), "success")
|
||||||
return redirect(url_for("accounting-report.default"))
|
return redirect(url_for("accounting-report.default"))
|
||||||
|
|
||||||
|
|
||||||
def __populate_sample_data() -> None:
|
|
||||||
"""Populates the sample data.
|
|
||||||
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
from accounting.models import Account, JournalEntry, JournalEntryLineItem
|
|
||||||
data_dir: Path = Path(__file__).parent / "data"
|
|
||||||
today: date = date.today()
|
|
||||||
user: User | None = current_user()
|
|
||||||
assert user is not None
|
|
||||||
|
|
||||||
def filter_journal_entry(data: dict[str, t.Any]) -> dict[str, t.Any]:
|
|
||||||
"""Filters the journal entry data from JSON.
|
|
||||||
|
|
||||||
:param data: The journal entry data.
|
|
||||||
:return: The journal entry data from JSON.
|
|
||||||
"""
|
|
||||||
data = data.copy()
|
|
||||||
data["id"] = int(data["id"])
|
|
||||||
data["date"] = today - timedelta(days=int(data["date"]))
|
|
||||||
data["no"] = int(data["no"])
|
|
||||||
if data["note"] == "":
|
|
||||||
data["note"] = None
|
|
||||||
data["created_by_id"] = user.id
|
|
||||||
data["updated_by_id"] = user.id
|
|
||||||
return data
|
|
||||||
|
|
||||||
def filter_line_item(data: dict[str, t.Any]) -> dict[str, t.Any]:
|
|
||||||
"""Filters the journal entry line item data from JSON.
|
|
||||||
|
|
||||||
:param data: The journal entry line item data.
|
|
||||||
:return: The journal entry line item data from JSON.
|
|
||||||
"""
|
|
||||||
data = data.copy()
|
|
||||||
data["id"] = int(data["id"])
|
|
||||||
data["journal_entry_id"] = int(data["journal_entry_id"])
|
|
||||||
if data["original_line_item_id"] == "":
|
|
||||||
data["original_line_item_id"] = None
|
|
||||||
else:
|
|
||||||
data["original_line_item_id"] = int(data["original_line_item_id"])
|
|
||||||
data["is_debit"] = bool(data["is_debit"])
|
|
||||||
data["no"] = int(data["no"])
|
|
||||||
data["account_id"] = Account.find_by_code(data["account_id"]).id
|
|
||||||
if data["description"] == "":
|
|
||||||
data["description"] = None
|
|
||||||
data["amount"] = Decimal(data["amount"])
|
|
||||||
return data
|
|
||||||
|
|
||||||
def import_journal_entries(file: Path) -> None:
|
|
||||||
"""Imports the journal entries.
|
|
||||||
|
|
||||||
:param file: The CSV file.
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
with open(file) as fp:
|
|
||||||
reader: csv.DictReader = csv.DictReader(fp)
|
|
||||||
db.session.execute(sa.insert(JournalEntry),
|
|
||||||
[filter_journal_entry(x) for x in reader])
|
|
||||||
|
|
||||||
def import_line_items(file: Path) -> None:
|
|
||||||
"""Imports the journal entry line items.
|
|
||||||
|
|
||||||
:param file: The CSV file.
|
|
||||||
:return: None.
|
|
||||||
"""
|
|
||||||
with open(file) as fp:
|
|
||||||
reader: csv.DictReader = csv.DictReader(fp)
|
|
||||||
db.session.execute(sa.insert(JournalEntryLineItem),
|
|
||||||
[filter_line_item(x) for x in reader])
|
|
||||||
|
|
||||||
import_journal_entries(data_dir / "sample-journal_entries.csv")
|
|
||||||
import_line_items(data_dir / "sample-journal_entry_line_items.csv")
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
|
|
||||||
def __reset_database() -> None:
|
def __reset_database() -> None:
|
||||||
"""Resets the database.
|
"""Resets the database.
|
||||||
|
|
||||||
@ -167,6 +87,273 @@ def __reset_database() -> None:
|
|||||||
init_base_accounts_command()
|
init_base_accounts_command()
|
||||||
init_accounts_command(session["user"])
|
init_accounts_command(session["user"])
|
||||||
init_currencies_command(session["user"])
|
init_currencies_command(session["user"])
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
class SampleData(BaseTestData):
|
||||||
|
"""The sample data."""
|
||||||
|
|
||||||
|
def _init_data(self) -> None:
|
||||||
|
self.__add_recurring()
|
||||||
|
self.__add_offsets()
|
||||||
|
self.__add_meals()
|
||||||
|
|
||||||
|
def __add_recurring(self) -> None:
|
||||||
|
"""Adds the recurring data.
|
||||||
|
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
self.__add_usd_recurring()
|
||||||
|
self.__add_twd_recurring()
|
||||||
|
|
||||||
|
def __add_usd_recurring(self) -> None:
|
||||||
|
"""Adds the recurring data in USD.
|
||||||
|
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
today: date = date.today()
|
||||||
|
days: int
|
||||||
|
year: int
|
||||||
|
month: int
|
||||||
|
|
||||||
|
# Recurring in USD
|
||||||
|
j_date: date = date(today.year - 5, today.month, today.day)
|
||||||
|
j_date = j_date + timedelta(days=(4 - j_date.weekday()))
|
||||||
|
days = (today - j_date).days
|
||||||
|
while True:
|
||||||
|
if days < 0:
|
||||||
|
break
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "USD", "2600",
|
||||||
|
Accounts.BANK, "Transfer", Accounts.SERVICE, "Payroll")
|
||||||
|
|
||||||
|
days = days - 1
|
||||||
|
if days < 0:
|
||||||
|
break
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "USD", "1200",
|
||||||
|
Accounts.CASH, None, Accounts.BANK, "Withdraw")
|
||||||
|
days = days - 13
|
||||||
|
|
||||||
|
year = today.year - 5
|
||||||
|
month = today.month
|
||||||
|
while True:
|
||||||
|
month = month + 1
|
||||||
|
if month > 12:
|
||||||
|
year = year + 1
|
||||||
|
month = 1
|
||||||
|
days = (today - date(year, month, 1)).days
|
||||||
|
if days < 0:
|
||||||
|
break
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "USD", "1800",
|
||||||
|
Accounts.RENT_EXPENSE, "Rent", Accounts.BANK, "Transfer")
|
||||||
|
|
||||||
|
def __add_twd_recurring(self) -> None:
|
||||||
|
"""Adds the recurring data in TWD.
|
||||||
|
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
today: date = date.today()
|
||||||
|
|
||||||
|
year: int = today.year - 5
|
||||||
|
month: int = today.month
|
||||||
|
while True:
|
||||||
|
days: int = (today - date(year, month, 5)).days
|
||||||
|
if days < 0:
|
||||||
|
break
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "TWD", "50000",
|
||||||
|
Accounts.BANK, "薪資轉帳", Accounts.SERVICE, "薪水")
|
||||||
|
|
||||||
|
days = days - 1
|
||||||
|
if days < 0:
|
||||||
|
break
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "TWD", "25000",
|
||||||
|
Accounts.CASH, None, Accounts.BANK, "提款")
|
||||||
|
|
||||||
|
days = days - 4
|
||||||
|
if days < 0:
|
||||||
|
break
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "TWD", "18000",
|
||||||
|
Accounts.RENT_EXPENSE, "房租", Accounts.BANK, "轉帳")
|
||||||
|
|
||||||
|
month = month + 1
|
||||||
|
if month > 12:
|
||||||
|
year = year + 1
|
||||||
|
month = 1
|
||||||
|
|
||||||
|
def __add_offsets(self) -> None:
|
||||||
|
"""Adds the offset data.
|
||||||
|
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
days: int
|
||||||
|
year: int
|
||||||
|
month: int
|
||||||
|
description: str
|
||||||
|
line_item_or: JournalEntryLineItemData
|
||||||
|
line_item_of: JournalEntryLineItemData
|
||||||
|
|
||||||
|
# Full offset and unmatched in USD
|
||||||
|
description = "Speaking—Institute"
|
||||||
|
line_item_or = JournalEntryLineItemData(
|
||||||
|
Accounts.RECEIVABLE, description, "120")
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
40, [JournalEntryCurrencyData(
|
||||||
|
"USD", [line_item_or], [JournalEntryLineItemData(
|
||||||
|
Accounts.SERVICE, description, "120")])]))
|
||||||
|
line_item_of = JournalEntryLineItemData(
|
||||||
|
Accounts.RECEIVABLE, description, "120",
|
||||||
|
original_line_item=line_item_or)
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
5, [JournalEntryCurrencyData(
|
||||||
|
"USD", [JournalEntryLineItemData(
|
||||||
|
Accounts.BANK, description, "120")],
|
||||||
|
[line_item_of])]))
|
||||||
|
self.__add_journal_entry(
|
||||||
|
30, "USD", "120",
|
||||||
|
Accounts.BANK, description, Accounts.SERVICE, description)
|
||||||
|
|
||||||
|
# Partial offset in USD
|
||||||
|
line_item_or = JournalEntryLineItemData(
|
||||||
|
Accounts.PAYABLE, "Computer", "1600")
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
60, [JournalEntryCurrencyData(
|
||||||
|
"USD", [JournalEntryLineItemData(
|
||||||
|
Accounts.MACHINERY, "Computer", "1600")],
|
||||||
|
[line_item_or])]))
|
||||||
|
line_item_of = JournalEntryLineItemData(
|
||||||
|
Accounts.PAYABLE, "Computer", "800",
|
||||||
|
original_line_item=line_item_or)
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
35, [JournalEntryCurrencyData(
|
||||||
|
"USD", [line_item_of], [JournalEntryLineItemData(
|
||||||
|
Accounts.BANK, "Computer", "800")])]))
|
||||||
|
line_item_of = JournalEntryLineItemData(
|
||||||
|
Accounts.PAYABLE, "Computer", "400",
|
||||||
|
original_line_item=line_item_or)
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
10, [JournalEntryCurrencyData(
|
||||||
|
"USD", [line_item_of], [JournalEntryLineItemData(
|
||||||
|
Accounts.CASH, "Computer", "400")])]))
|
||||||
|
|
||||||
|
# Full offset and unmatched in TWD
|
||||||
|
description = "演講費—母校"
|
||||||
|
line_item_or = JournalEntryLineItemData(
|
||||||
|
Accounts.RECEIVABLE, description, "3000")
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
45, [JournalEntryCurrencyData(
|
||||||
|
"TWD", [line_item_or], [JournalEntryLineItemData(
|
||||||
|
Accounts.SERVICE, description, "3000")])]))
|
||||||
|
line_item_of = JournalEntryLineItemData(
|
||||||
|
Accounts.RECEIVABLE, description, "3000",
|
||||||
|
original_line_item=line_item_or)
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
6, [JournalEntryCurrencyData(
|
||||||
|
"TWD", [JournalEntryLineItemData(
|
||||||
|
Accounts.BANK, description, "3000")],
|
||||||
|
[line_item_of])]))
|
||||||
|
self.__add_journal_entry(
|
||||||
|
25, "TWD", "3000",
|
||||||
|
Accounts.BANK, description, Accounts.SERVICE, description)
|
||||||
|
|
||||||
|
# Partial offset in TWD
|
||||||
|
line_item_or = JournalEntryLineItemData(
|
||||||
|
Accounts.PAYABLE, "手機", "30000")
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
55, [JournalEntryCurrencyData(
|
||||||
|
"TWD", [JournalEntryLineItemData(
|
||||||
|
Accounts.MACHINERY, "手機", "30000")],
|
||||||
|
[line_item_or])]))
|
||||||
|
line_item_of = JournalEntryLineItemData(
|
||||||
|
Accounts.PAYABLE, "手機", "16000",
|
||||||
|
original_line_item=line_item_or)
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
27, [JournalEntryCurrencyData(
|
||||||
|
"TWD", [line_item_of], [JournalEntryLineItemData(
|
||||||
|
Accounts.BANK, "手機", "16000")])]))
|
||||||
|
line_item_of = JournalEntryLineItemData(
|
||||||
|
Accounts.PAYABLE, "手機", "6000",
|
||||||
|
original_line_item=line_item_or)
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
8, [JournalEntryCurrencyData(
|
||||||
|
"TWD", [line_item_of], [JournalEntryLineItemData(
|
||||||
|
Accounts.CASH, "手機", "6000")])]))
|
||||||
|
|
||||||
|
def __add_meals(self) -> None:
|
||||||
|
"""Adds the meal data.
|
||||||
|
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
days = 60
|
||||||
|
while days >= 0:
|
||||||
|
# Meals in USD
|
||||||
|
if days % 4 == 2:
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "USD", "2.9",
|
||||||
|
Accounts.MEAL, "Lunch—Coffee", Accounts.CASH, None)
|
||||||
|
else:
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "USD", "3.9",
|
||||||
|
Accounts.MEAL, "Lunch—Coffee", Accounts.CASH, None)
|
||||||
|
|
||||||
|
if days % 15 == 3:
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "USD", "5.45",
|
||||||
|
Accounts.MEAL, "Dinner—Pizza",
|
||||||
|
Accounts.PAYABLE, "Dinner—Pizza")
|
||||||
|
else:
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "USD", "5.9",
|
||||||
|
Accounts.MEAL, "Dinner—Pasta", Accounts.CASH, None)
|
||||||
|
|
||||||
|
# Meals in TWD
|
||||||
|
if days % 5 == 3:
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "TWD", "125",
|
||||||
|
Accounts.MEAL, "午餐—鄰家咖啡", Accounts.CASH, None)
|
||||||
|
else:
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "TWD", "80",
|
||||||
|
Accounts.MEAL, "午餐—便當", Accounts.CASH, None)
|
||||||
|
|
||||||
|
if days % 15 == 3:
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "TWD", "320",
|
||||||
|
Accounts.MEAL, "晚餐—牛排", Accounts.PAYABLE, "晚餐—牛排")
|
||||||
|
else:
|
||||||
|
self.__add_journal_entry(
|
||||||
|
days, "TWD", "100",
|
||||||
|
Accounts.MEAL, "晚餐—自助餐", Accounts.CASH, None)
|
||||||
|
|
||||||
|
days = days - 1
|
||||||
|
|
||||||
|
def __add_journal_entry(
|
||||||
|
self, days: int, currency: str, amount: str,
|
||||||
|
debit_account: str, debit_description: str | None,
|
||||||
|
credit_account: str, credit_description: str | None) -> None:
|
||||||
|
"""Adds a simple journal entry.
|
||||||
|
|
||||||
|
:param days: The number of days before today.
|
||||||
|
:param currency: The currency code.
|
||||||
|
:param amount: The amount.
|
||||||
|
:param debit_account: The debit account code.
|
||||||
|
:param debit_description: The debit description.
|
||||||
|
:param credit_account: The credit account code.
|
||||||
|
:param credit_description: The credit description.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
self._add_journal_entry(JournalEntryData(
|
||||||
|
days,
|
||||||
|
[JournalEntryCurrencyData(
|
||||||
|
currency,
|
||||||
|
[JournalEntryLineItemData(
|
||||||
|
debit_account, debit_description, amount)],
|
||||||
|
[JournalEntryLineItemData(
|
||||||
|
credit_account, credit_description, amount)])]))
|
||||||
|
|
||||||
|
|
||||||
def init_app(app: Flask) -> None:
|
def init_app(app: Flask) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user