Compare commits
6 Commits
7c512b1c15
...
v0.9.1
Author | SHA1 | Date | |
---|---|---|---|
bf2c7bb785 | |||
93ba086548 | |||
5c4f6017b8 | |||
cb16b2f0ff | |||
d2f11e8779 | |||
4ccaf01b3c |
@ -13,7 +13,7 @@ sys.path.insert(0, os.path.abspath('../../src/'))
|
|||||||
project = 'Mia! Accounting Flask'
|
project = 'Mia! Accounting Flask'
|
||||||
copyright = '2023, imacat'
|
copyright = '2023, imacat'
|
||||||
author = 'imacat'
|
author = 'imacat'
|
||||||
release = '0.9.0'
|
release = '0.9.1'
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
name = mia-accounting-flask
|
name = mia-accounting-flask
|
||||||
version = 0.9.0
|
version = 0.9.1
|
||||||
author = imacat
|
author = imacat
|
||||||
author_email = imacat@mail.imacat.idv.tw
|
author_email = imacat@mail.imacat.idv.tw
|
||||||
description = The Mia! Accounting Flask project.
|
description = The Mia! Accounting Flask project.
|
||||||
|
@ -770,17 +770,10 @@ class JournalEntryLineItem(db.Model):
|
|||||||
frac: Decimal = (value - whole).normalize()
|
frac: Decimal = (value - whole).normalize()
|
||||||
return str(whole) + str(abs(frac))[1:]
|
return str(whole) + str(abs(frac))[1:]
|
||||||
|
|
||||||
journal_entry_day: date = self.journal_entry.date
|
return ["{}/{}/{}".format(self.journal_entry.date.year,
|
||||||
description: str = "" if self.description is None else self.description
|
self.journal_entry.date.month,
|
||||||
return [description,
|
self.journal_entry.date.day),
|
||||||
str(journal_entry_day.year),
|
"" if self.description is None else self.description,
|
||||||
"{}/{}".format(journal_entry_day.year,
|
|
||||||
journal_entry_day.month),
|
|
||||||
"{}/{}".format(journal_entry_day.month,
|
|
||||||
journal_entry_day.day),
|
|
||||||
"{}/{}/{}".format(journal_entry_day.year,
|
|
||||||
journal_entry_day.month,
|
|
||||||
journal_entry_day.day),
|
|
||||||
format_amount(self.amount)]
|
format_amount(self.amount)]
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,26 +38,49 @@ First written: 2023/3/22
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<table class="table table-striped table-hover table-light" aria-label="{{ A_("Settings") }}">
|
||||||
<input id="accounting-default-currency" class="form-control" value="{{ obj.default_currency_text }}" readonly="readonly">
|
<tbody>
|
||||||
<label class="form-label" for="accounting-default-currency">{{ A_("Default Currency") }}</label>
|
<tr>
|
||||||
</div>
|
<th scope="row">{{ A_("Default Currency") }}</th>
|
||||||
|
<td>{{ obj.default_currency_text }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{ A_("Default Account for the Income and Expenses Log") }}</th>
|
||||||
|
<td>{{ obj.default_ie_account_code_text }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<h2>{{ A_("Recurring Expense") }}</h2>
|
||||||
<input id="accounting-default-ie-account" class="form-control" value="{{ obj.default_ie_account_code_text }}" readonly="readonly">
|
|
||||||
<label class="form-label" for="accounting-default-ie-account">{{ A_("Default Account for the Income and Expenses Log") }}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% with expense_income = "expense",
|
{% if obj.recurring.expenses %}
|
||||||
label = A_("Recurring Expense"),
|
<ul class="list-group mb-3 accounting-list-group-stripped accounting-list-group-hover">
|
||||||
recurring_items = obj.recurring.expenses %}
|
{% for recurring_item in obj.recurring.expenses %}
|
||||||
{% include "accounting/option/include/detail-recurring-expense-income.html" %}
|
<li class="list-group-item">
|
||||||
{% endwith %}
|
<div class="small">{{ recurring_item.account_text }}</div>
|
||||||
|
<div>{{ recurring_item.name }}</div>
|
||||||
|
<div class="small">{{ recurring_item.description_template }}</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p>{{ A_("There is no data.") }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% with expense_income = "income",
|
<h2>{{ A_("Recurring Income") }}</h2>
|
||||||
label = A_("Recurring Income"),
|
|
||||||
recurring_items = obj.recurring.incomes %}
|
{% if obj.recurring.incomes %}
|
||||||
{% include "accounting/option/include/detail-recurring-expense-income.html" %}
|
<ul class="list-group mb-3 accounting-list-group-stripped accounting-list-group-hover">
|
||||||
{% endwith %}
|
{% for recurring_item in obj.recurring.incomes %}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="small">{{ recurring_item.account_text }}</div>
|
||||||
|
<div>{{ recurring_item.name }}</div>
|
||||||
|
<div class="small">{{ recurring_item.description_template }}</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p>{{ A_("There is no data.") }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
{#
|
|
||||||
The Mia! Accounting Flask Project
|
|
||||||
detail-recurring-expense-income.html: The recurring expense or income in the option detail
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
|
||||||
First written: 2023/3/22
|
|
||||||
#}
|
|
||||||
<div id="accounting-recurring-{{ expense_income }}" class="form-control mb-3 accounting-material-text-field {% if recurring_items %} accounting-not-empty {% endif %}">
|
|
||||||
<label class="form-label" for="accounting-recurring-{{ expense_income }}">{{ label }}</label>
|
|
||||||
{% if recurring_items %}
|
|
||||||
<ul class="list-group mb-2 mt-2">
|
|
||||||
{% for item in recurring_items %}
|
|
||||||
{% include "accounting/option/include/detail-recurring-item.html" %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
@ -1,28 +0,0 @@
|
|||||||
{#
|
|
||||||
The Mia! Accounting Flask Project
|
|
||||||
detail-recurring-item.html: The recurring item in the option detail
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
|
||||||
First written: 2023/3/22
|
|
||||||
#}
|
|
||||||
{# <ul> For SonarQube not to complain about incorrect HTML #}
|
|
||||||
<li class="list-group-item list-group-item-action">
|
|
||||||
<div class="small">{{ item.account_text }}</div>
|
|
||||||
<div>{{ item.name }}</div>
|
|
||||||
<div class="small">{{ item.description_template }}</div>
|
|
||||||
</li>
|
|
||||||
{# </ul> For SonarQube not to complain about incorrect HTML #}
|
|
@ -547,8 +547,8 @@ class AccountTestCase(unittest.TestCase):
|
|||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
from accounting.models import Account
|
from accounting.models import Account
|
||||||
editor_username, editor2_username = "editor", "editor2"
|
editor_username, admin_username = "editor", "admin"
|
||||||
client, csrf_token = get_client(self.app, editor2_username)
|
client, csrf_token = get_client(self.app, admin_username)
|
||||||
detail_uri: str = f"{PREFIX}/{CASH.code}"
|
detail_uri: str = f"{PREFIX}/{CASH.code}"
|
||||||
update_uri: str = f"{PREFIX}/{CASH.code}/update"
|
update_uri: str = f"{PREFIX}/{CASH.code}/update"
|
||||||
account: Account
|
account: Account
|
||||||
@ -571,7 +571,7 @@ class AccountTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(account.created_by.username,
|
self.assertEqual(account.created_by.username,
|
||||||
editor_username)
|
editor_username)
|
||||||
self.assertEqual(account.updated_by.username,
|
self.assertEqual(account.updated_by.username,
|
||||||
editor2_username)
|
admin_username)
|
||||||
|
|
||||||
def test_l10n(self) -> None:
|
def test_l10n(self) -> None:
|
||||||
"""Tests the localization.
|
"""Tests the localization.
|
||||||
|
@ -471,8 +471,8 @@ class CurrencyTestCase(unittest.TestCase):
|
|||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
from accounting.models import Currency
|
from accounting.models import Currency
|
||||||
editor_username, editor2_username = "editor", "editor2"
|
editor_username, admin_username = "editor", "admin"
|
||||||
client, csrf_token = get_client(self.app, editor2_username)
|
client, csrf_token = get_client(self.app, admin_username)
|
||||||
detail_uri: str = f"{PREFIX}/{USD.code}"
|
detail_uri: str = f"{PREFIX}/{USD.code}"
|
||||||
update_uri: str = f"{PREFIX}/{USD.code}/update"
|
update_uri: str = f"{PREFIX}/{USD.code}/update"
|
||||||
currency: Currency
|
currency: Currency
|
||||||
@ -493,7 +493,7 @@ class CurrencyTestCase(unittest.TestCase):
|
|||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
currency = db.session.get(Currency, USD.code)
|
currency = db.session.get(Currency, USD.code)
|
||||||
self.assertEqual(currency.created_by.username, editor_username)
|
self.assertEqual(currency.created_by.username, editor_username)
|
||||||
self.assertEqual(currency.updated_by.username, editor2_username)
|
self.assertEqual(currency.updated_by.username, admin_username)
|
||||||
|
|
||||||
def test_api_exists(self) -> None:
|
def test_api_exists(self) -> None:
|
||||||
"""Tests the API to check if a code exists.
|
"""Tests the API to check if a code exists.
|
||||||
|
@ -537,8 +537,8 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase):
|
|||||||
from accounting.models import JournalEntry
|
from accounting.models import JournalEntry
|
||||||
journal_entry_id: int \
|
journal_entry_id: int \
|
||||||
= add_journal_entry(self.client, self.__get_add_form())
|
= add_journal_entry(self.client, self.__get_add_form())
|
||||||
editor_username, editor2_username = "editor", "editor2"
|
editor_username, admin_username = "editor", "admin"
|
||||||
client, csrf_token = get_client(self.app, editor2_username)
|
client, csrf_token = get_client(self.app, admin_username)
|
||||||
detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next"
|
detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next"
|
||||||
update_uri: str = f"{PREFIX}/{journal_entry_id}/update"
|
update_uri: str = f"{PREFIX}/{journal_entry_id}/update"
|
||||||
journal_entry: JournalEntry
|
journal_entry: JournalEntry
|
||||||
@ -562,7 +562,7 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(journal_entry.created_by.username,
|
self.assertEqual(journal_entry.created_by.username,
|
||||||
editor_username)
|
editor_username)
|
||||||
self.assertEqual(journal_entry.updated_by.username,
|
self.assertEqual(journal_entry.updated_by.username,
|
||||||
editor2_username)
|
admin_username)
|
||||||
|
|
||||||
def test_delete(self) -> None:
|
def test_delete(self) -> None:
|
||||||
"""Tests to delete a journal entry.
|
"""Tests to delete a journal entry.
|
||||||
@ -1163,8 +1163,8 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase):
|
|||||||
from accounting.models import JournalEntry
|
from accounting.models import JournalEntry
|
||||||
journal_entry_id: int \
|
journal_entry_id: int \
|
||||||
= add_journal_entry(self.client, self.__get_add_form())
|
= add_journal_entry(self.client, self.__get_add_form())
|
||||||
editor_username, editor2_username = "editor", "editor2"
|
editor_username, admin_username = "editor", "admin"
|
||||||
client, csrf_token = get_client(self.app, editor2_username)
|
client, csrf_token = get_client(self.app, admin_username)
|
||||||
detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next"
|
detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next"
|
||||||
update_uri: str = f"{PREFIX}/{journal_entry_id}/update"
|
update_uri: str = f"{PREFIX}/{journal_entry_id}/update"
|
||||||
journal_entry: JournalEntry
|
journal_entry: JournalEntry
|
||||||
@ -1188,7 +1188,7 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(journal_entry.created_by.username,
|
self.assertEqual(journal_entry.created_by.username,
|
||||||
editor_username)
|
editor_username)
|
||||||
self.assertEqual(journal_entry.updated_by.username,
|
self.assertEqual(journal_entry.updated_by.username,
|
||||||
editor2_username)
|
admin_username)
|
||||||
|
|
||||||
def test_delete(self) -> None:
|
def test_delete(self) -> None:
|
||||||
"""Tests to delete a journal entry.
|
"""Tests to delete a journal entry.
|
||||||
@ -1837,8 +1837,8 @@ class TransferJournalEntryTestCase(unittest.TestCase):
|
|||||||
from accounting.models import JournalEntry
|
from accounting.models import JournalEntry
|
||||||
journal_entry_id: int \
|
journal_entry_id: int \
|
||||||
= add_journal_entry(self.client, self.__get_add_form())
|
= add_journal_entry(self.client, self.__get_add_form())
|
||||||
editor_username, editor2_username = "editor", "editor2"
|
editor_username, admin_username = "editor", "admin"
|
||||||
client, csrf_token = get_client(self.app, editor2_username)
|
client, csrf_token = get_client(self.app, admin_username)
|
||||||
detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next"
|
detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next"
|
||||||
update_uri: str = f"{PREFIX}/{journal_entry_id}/update"
|
update_uri: str = f"{PREFIX}/{journal_entry_id}/update"
|
||||||
journal_entry: JournalEntry
|
journal_entry: JournalEntry
|
||||||
@ -1862,7 +1862,7 @@ class TransferJournalEntryTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(journal_entry.created_by.username,
|
self.assertEqual(journal_entry.created_by.username,
|
||||||
editor_username)
|
editor_username)
|
||||||
self.assertEqual(journal_entry.updated_by.username,
|
self.assertEqual(journal_entry.updated_by.username,
|
||||||
editor2_username)
|
admin_username)
|
||||||
|
|
||||||
def test_save_as_receipt(self) -> None:
|
def test_save_as_receipt(self) -> None:
|
||||||
"""Tests to save a transfer journal entry as a cash receipt journal
|
"""Tests to save a transfer journal entry as a cash receipt journal
|
||||||
|
@ -67,7 +67,7 @@ class OptionTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(result.exit_code, 0)
|
self.assertEqual(result.exit_code, 0)
|
||||||
Option.query.delete()
|
Option.query.delete()
|
||||||
|
|
||||||
self.client, self.csrf_token = get_client(self.app, "editor")
|
self.client, self.csrf_token = get_client(self.app, "admin")
|
||||||
self.data: TestData = TestData(self.app, self.client, self.csrf_token)
|
self.data: TestData = TestData(self.app, self.client, self.csrf_token)
|
||||||
|
|
||||||
def test_nobody(self) -> None:
|
def test_nobody(self) -> None:
|
||||||
@ -104,12 +104,12 @@ class OptionTestCase(unittest.TestCase):
|
|||||||
response = client.post(UPDATE_URI, data=self.__get_form(csrf_token))
|
response = client.post(UPDATE_URI, data=self.__get_form(csrf_token))
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
def test_editor2(self) -> None:
|
def test_editor(self) -> None:
|
||||||
"""Test the permission as non-administrator.
|
"""Test the permission as editor.
|
||||||
|
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
client, csrf_token = get_client(self.app, "editor2")
|
client, csrf_token = get_client(self.app, "editor")
|
||||||
response: httpx.Response
|
response: httpx.Response
|
||||||
|
|
||||||
response = client.get(DETAIL_URI)
|
response = client.get(DETAIL_URI)
|
||||||
@ -121,7 +121,7 @@ class OptionTestCase(unittest.TestCase):
|
|||||||
response = client.post(UPDATE_URI, data=self.__get_form(csrf_token))
|
response = client.post(UPDATE_URI, data=self.__get_form(csrf_token))
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
def test_editor(self) -> None:
|
def test_admin(self) -> None:
|
||||||
"""Test the permission as administrator.
|
"""Test the permission as administrator.
|
||||||
|
|
||||||
:return: None.
|
:return: None.
|
||||||
@ -343,7 +343,7 @@ class OptionTestCase(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
from accounting.models import Option
|
from accounting.models import Option
|
||||||
from accounting.utils.user import get_user_pk
|
from accounting.utils.user import get_user_pk
|
||||||
editor_username, editor2_username = "editor", "editor2"
|
admin_username, editor_username = "admin", "editor"
|
||||||
option: Option | None
|
option: Option | None
|
||||||
response: httpx.Response
|
response: httpx.Response
|
||||||
|
|
||||||
@ -352,11 +352,11 @@ class OptionTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(response.headers["Location"], DETAIL_URI)
|
self.assertEqual(response.headers["Location"], DETAIL_URI)
|
||||||
|
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
editor2_pk: int = get_user_pk(editor2_username)
|
editor_pk: int = get_user_pk(editor_username)
|
||||||
option = db.session.get(Option, "recurring")
|
option = db.session.get(Option, "recurring")
|
||||||
self.assertIsNotNone(option)
|
self.assertIsNotNone(option)
|
||||||
option.created_by_id = editor2_pk
|
option.created_by_id = editor_pk
|
||||||
option.updated_by_id = editor2_pk
|
option.updated_by_id = editor_pk
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
form: dict[str, str] = self.__get_form()
|
form: dict[str, str] = self.__get_form()
|
||||||
@ -371,8 +371,8 @@ class OptionTestCase(unittest.TestCase):
|
|||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
option = db.session.get(Option, "recurring")
|
option = db.session.get(Option, "recurring")
|
||||||
self.assertIsNotNone(option)
|
self.assertIsNotNone(option)
|
||||||
self.assertEqual(option.created_by.username, editor2_username)
|
self.assertEqual(option.created_by.username, editor_username)
|
||||||
self.assertEqual(option.updated_by.username, editor_username)
|
self.assertEqual(option.updated_by.username, admin_username)
|
||||||
|
|
||||||
def __get_form(self, csrf_token: str | None = None) -> dict[str, str]:
|
def __get_form(self, csrf_token: str | None = None) -> dict[str, str]:
|
||||||
"""Returns the option form.
|
"""Returns the option form.
|
||||||
|
@ -72,15 +72,15 @@ def create_app(is_testing: bool = False) -> Flask:
|
|||||||
def can_view(self) -> bool:
|
def can_view(self) -> bool:
|
||||||
return auth.current_user() is not None \
|
return auth.current_user() is not None \
|
||||||
and auth.current_user().username in ["viewer", "editor",
|
and auth.current_user().username in ["viewer", "editor",
|
||||||
"editor2"]
|
"admin"]
|
||||||
|
|
||||||
def can_edit(self) -> bool:
|
def can_edit(self) -> bool:
|
||||||
return auth.current_user() is not None \
|
return auth.current_user() is not None \
|
||||||
and auth.current_user().username in ["editor", "editor2"]
|
and auth.current_user().username in ["editor", "admin"]
|
||||||
|
|
||||||
def can_admin(self) -> bool:
|
def can_admin(self) -> bool:
|
||||||
return auth.current_user() is not None \
|
return auth.current_user() is not None \
|
||||||
and auth.current_user().username == "editor"
|
and auth.current_user().username == "admin"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cls(self) -> t.Type[auth.User]:
|
def cls(self) -> t.Type[auth.User]:
|
||||||
@ -112,7 +112,7 @@ def init_db_command() -> None:
|
|||||||
"""Initializes the database."""
|
"""Initializes the database."""
|
||||||
db.create_all()
|
db.create_all()
|
||||||
from .auth import User
|
from .auth import User
|
||||||
for username in ["viewer", "editor", "editor2", "nobody"]:
|
for username in ["viewer", "editor", "admin", "nobody"]:
|
||||||
if User.query.filter(User.username == username).first() is None:
|
if User.query.filter(User.username == username).first() is None:
|
||||||
db.session.add(User(username=username))
|
db.session.add(User(username=username))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -58,8 +58,8 @@ def login() -> redirect:
|
|||||||
|
|
||||||
:return: The redirection to the home page.
|
:return: The redirection to the home page.
|
||||||
"""
|
"""
|
||||||
if request.form.get("username") not in ["viewer", "editor", "editor2",
|
if request.form.get("username") not in {"viewer", "editor", "admin",
|
||||||
"nobody"]:
|
"nobody"}:
|
||||||
return redirect(url_for("auth.login"))
|
return redirect(url_for("auth.login"))
|
||||||
session["user"] = request.form.get("username")
|
session["user"] = request.form.get("username")
|
||||||
return redirect(url_for("home.home"))
|
return redirect(url_for("home.home"))
|
||||||
|
@ -29,7 +29,7 @@ First written: 2023/1/27
|
|||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<button class="btn btn-primary" type="submit" name="username" value="viewer">{{ _("Viewer") }}</button>
|
<button class="btn btn-primary" type="submit" name="username" value="viewer">{{ _("Viewer") }}</button>
|
||||||
<button class="btn btn-primary" type="submit" name="username" value="editor">{{ _("Editor") }}</button>
|
<button class="btn btn-primary" type="submit" name="username" value="editor">{{ _("Editor") }}</button>
|
||||||
<button class="btn btn-primary" type="submit" name="username" value="editor2">{{ _("Editor2") }}</button>
|
<button class="btn btn-primary" type="submit" name="username" value="admin">{{ _("Administrator") }}</button>
|
||||||
<button class="btn btn-primary" type="submit" name="username" value="nobody">{{ _("Nobody") }}</button>
|
<button class="btn btn-primary" type="submit" name="username" value="nobody">{{ _("Nobody") }}</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Mia! Accounting Flask Demonstration 0.0.0\n"
|
"Project-Id-Version: Mia! Accounting Flask Demonstration 0.0.0\n"
|
||||||
"Report-Msgid-Bugs-To: imacat@mail.imacat.idv.tw\n"
|
"Report-Msgid-Bugs-To: imacat@mail.imacat.idv.tw\n"
|
||||||
"POT-Creation-Date: 2023-02-27 10:07+0800\n"
|
"POT-Creation-Date: 2023-03-24 08:32+0800\n"
|
||||||
"PO-Revision-Date: 2023-02-27 10:08+0800\n"
|
"PO-Revision-Date: 2023-03-24 08:33+0800\n"
|
||||||
"Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n"
|
"Last-Translator: imacat <imacat@mail.imacat.idv.tw>\n"
|
||||||
"Language: zh_Hant\n"
|
"Language: zh_Hant\n"
|
||||||
"Language-Team: zh_Hant <imacat@mail.imacat.idv.tw>\n"
|
"Language-Team: zh_Hant <imacat@mail.imacat.idv.tw>\n"
|
||||||
@ -18,27 +18,27 @@ msgstr ""
|
|||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=utf-8\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: Babel 2.11.0\n"
|
"Generated-By: Babel 2.12.1\n"
|
||||||
|
|
||||||
#: tests/test_site/templates/base.html:23
|
#: tests/test_site/templates/base.html:23
|
||||||
msgid "en"
|
msgid "en"
|
||||||
msgstr "zh-Hant"
|
msgstr "zh-Hant"
|
||||||
|
|
||||||
#: tests/test_site/templates/base.html:43
|
#: tests/test_site/templates/base.html:46
|
||||||
#: tests/test_site/templates/home.html:24
|
#: tests/test_site/templates/home.html:24
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "首頁"
|
msgstr "首頁"
|
||||||
|
|
||||||
#: tests/test_site/templates/base.html:68
|
#: tests/test_site/templates/base.html:71
|
||||||
msgid "Log Out"
|
msgid "Log Out"
|
||||||
msgstr "登出"
|
msgstr "登出"
|
||||||
|
|
||||||
#: tests/test_site/templates/base.html:78
|
#: tests/test_site/templates/base.html:81
|
||||||
#: tests/test_site/templates/login.html:24
|
#: tests/test_site/templates/login.html:24
|
||||||
msgid "Log In"
|
msgid "Log In"
|
||||||
msgstr "登入"
|
msgstr "登入"
|
||||||
|
|
||||||
#: tests/test_site/templates/base.html:119
|
#: tests/test_site/templates/base.html:122
|
||||||
msgid "Error:"
|
msgid "Error:"
|
||||||
msgstr "錯誤:"
|
msgstr "錯誤:"
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ msgid "Editor"
|
|||||||
msgstr "記帳者"
|
msgstr "記帳者"
|
||||||
|
|
||||||
#: tests/test_site/templates/login.html:32
|
#: tests/test_site/templates/login.html:32
|
||||||
msgid "Editor2"
|
msgid "Administrator"
|
||||||
msgstr "記帳者2"
|
msgstr "管理者"
|
||||||
|
|
||||||
#: tests/test_site/templates/login.html:33
|
#: tests/test_site/templates/login.html:33
|
||||||
msgid "Nobody"
|
msgid "Nobody"
|
||||||
|
Reference in New Issue
Block a user