Skip to content

Commit e63e592

Browse files
committed
Added option to set limits per category.
1 parent 1858e43 commit e63e592

File tree

3 files changed

+142
-26
lines changed

3 files changed

+142
-26
lines changed

app/handlers/books.py

Lines changed: 106 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Handlers for reports workflow."""
22

3+
import json
34
import re
45
from datetime import datetime
56
from typing import Optional
@@ -35,6 +36,7 @@ class BooksState(StatesGroup):
3536
parent_category = State()
3637
category = State()
3738
category_title = State()
39+
category_limit = State()
3840

3941

4042
class Books(HandlerBase):
@@ -52,6 +54,7 @@ def __init__(self, db: models.DB, dp: Dispatcher, router: Router) -> None:
5254
router.callback_query.register(self.category_type_callback, BooksState.category_type)
5355
router.callback_query.register(self.categories_callback, BooksState.category)
5456
router.message.register(self.category_title_message, BooksState.category_title)
57+
dp.message.register(self.category_limit_message, BooksState.category_limit)
5558
dp.message.register(self.join, Command('join'))
5659

5760
async def books(
@@ -647,10 +650,10 @@ async def _categories(
647650
text=__(messages.BUTTON_TITLE, lang=from_user.language_code),
648651
callback_data='/update_title'
649652
),
650-
# InlineKeyboardButton(
651-
# text=__(messages.BUTTON_LIMIT, lang=from_user.language_code),
652-
# callback_data='/set_limits'
653-
# ),
653+
InlineKeyboardButton(
654+
text=__(messages.BUTTON_LIMIT, lang=from_user.language_code),
655+
callback_data='/set_limit'
656+
),
654657
InlineKeyboardButton(
655658
text=__(messages.BUTTON_REMOVE, lang=from_user.language_code),
656659
callback_data='/delete'
@@ -711,10 +714,10 @@ async def categories_callback(self, call: CallbackQuery, state: FSMContext) -> N
711714
await state.update_data(category=data['parent_category'])
712715
await self.category_title(call.message, state, call.from_user)
713716
return
714-
# if call.data == '/set_limits':
715-
# await state.update_data(category=data['parent_category'])
716-
# await self.category_limits(call.message, state, call.from_user)
717-
# return
717+
if call.data == '/set_limit':
718+
await state.update_data(category=data['parent_category'])
719+
await self.category_limit(call.message, state, call.from_user)
720+
return
718721
if call.data == '/delete':
719722
category = self.db.get_category_by(
720723
book_id=book.id,
@@ -764,21 +767,101 @@ async def categories_callback(self, call: CallbackQuery, state: FSMContext) -> N
764767
await state.update_data(parent_category=category_id)
765768
await self._categories(call.message, state, call.from_user)
766769

767-
# async def category_limits(
768-
# self,
769-
# message: Message,
770-
# state: FSMContext,
771-
# from_user: Optional[User] = None
772-
# ) -> None:
773-
# """Displays message to request category limits."""
774-
# await state.set_state(BooksState.category_title)
775-
# from_user = from_user or message.from_user
776-
# await message.answer(
777-
# text=__(
778-
# text_dict=messages.CATEGORIES_SET_LIMITS,
779-
# lang=from_user.language_code
780-
# ).format(title=title),
781-
# )
770+
async def category_limit(
771+
self,
772+
message: Message,
773+
state: FSMContext,
774+
from_user: Optional[User] = None
775+
) -> None:
776+
"""Displays message to request category limit."""
777+
await state.set_state(BooksState.category_limit)
778+
from_user = from_user or message.from_user
779+
data = await state.get_data()
780+
book_id = int(data['book'])
781+
book = self.db.get_book_by(
782+
user_id=from_user.id,
783+
id=book_id,
784+
deleted=False
785+
)
786+
if not book:
787+
await self._invalid_request(message, state)
788+
return
789+
parent_category = None
790+
if 'parent_category' in data:
791+
parent_category = self.db.get_category_by(
792+
book_id=book.id,
793+
id=int(data['parent_category']),
794+
category_type=data['category_type'],
795+
deleted=False
796+
)
797+
if not parent_category:
798+
await self._invalid_request(message, state)
799+
return
800+
try:
801+
options = json.loads(parent_category.options)
802+
except Exception:
803+
options = {}
804+
monthly_limit = options.get('monthly_limit')
805+
if monthly_limit:
806+
monthly_limit_str = f'{monthly_limit:.2f} {book.currency}'
807+
else:
808+
monthly_limit_str = __(
809+
text_dict=messages.CATEGORIES_NO_LIMIT,
810+
lang=from_user.language_code
811+
)
812+
await message.answer(
813+
text=__(
814+
text_dict=messages.CATEGORIES_SET_LIMIT,
815+
lang=from_user.language_code
816+
).format(title=parent_category.title, limit=monthly_limit_str),
817+
)
818+
819+
async def category_limit_message(self, message: Message, state: FSMContext) -> None:
820+
"""Handles entered category limit."""
821+
data = await state.get_data()
822+
book_id = int(data['book'])
823+
book = self.db.get_book_by(
824+
user_id=message.from_user.id,
825+
id=book_id,
826+
deleted=False
827+
)
828+
if not book:
829+
await self._invalid_request(message, state)
830+
return
831+
parent_category = None
832+
if 'parent_category' in data:
833+
parent_category = self.db.get_category_by(
834+
book_id=book.id,
835+
id=int(data['parent_category']),
836+
category_type=data['category_type'],
837+
deleted=False
838+
)
839+
if not parent_category:
840+
await self._invalid_request(message, state)
841+
return
842+
amount_pattern = re.compile(r'^\d+\.{0,1}\d*$')
843+
limit_str = message.text.strip()
844+
if not amount_pattern.match(limit_str):
845+
await message.answer(
846+
text=__(
847+
text_dict=messages.CATEGORIES_LIMIT_INCORRECT,
848+
lang=message.from_user.language_code
849+
),
850+
)
851+
return
852+
try:
853+
options = json.loads(parent_category.options)
854+
except Exception:
855+
options = {}
856+
options['monthly_limit'] = float(limit_str)
857+
self.db.update_category(id=parent_category.id, options=json.dumps(options))
858+
await message.answer(
859+
text=__(
860+
text_dict=messages.CATEGORIES_LIMIT_UPDATED,
861+
lang=message.from_user.language_code
862+
),
863+
)
864+
await self._categories(message, state, message.from_user)
782865

783866
async def category_title(
784867
self,

app/handlers/expenses.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Handlers for expenses workflow."""
2+
import json
23

34
from datetime import datetime
45
from typing import Any, Optional
@@ -218,6 +219,18 @@ async def selector_categories_callback(
218219
)
219220
await state.clear()
220221
if category:
222+
try:
223+
options = json.loads(category.options)
224+
except Exception:
225+
options = {}
226+
monthly_limit = options.get('monthly_limit')
227+
if monthly_limit:
228+
monthly_limit_str = f'{monthly_limit:.2f} {book.currency}'
229+
else:
230+
monthly_limit_str = __(
231+
text_dict=messages.CATEGORIES_NO_LIMIT,
232+
lang=call.from_user.language_code,
233+
)
221234
await call.message.answer(
222235
text=__(
223236
text_dict=messages.EXPENSES_SUCCESSFULLY_CREATED_IN_CATEGORY,
@@ -233,6 +246,7 @@ async def selector_categories_callback(
233246
lang=call.from_user.language_code
234247
),
235248
total_amount='{:.2f}'.format(total_expenses or 0),
249+
monthly_limit=monthly_limit_str,
236250
)
237251
)
238252
else:

app/utils/messages.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,18 @@
176176
'ru': 'Категория <strong>{title}</strong> создана.',
177177
}
178178

179+
CATEGORIES_LIMIT_UPDATED = {
180+
'default': 'The limit successfully updated.',
181+
'ru': 'Лимит обновлен.',
182+
}
183+
184+
CATEGORIES_LIMIT_INCORRECT = {
185+
'default': (
186+
'Hm. Seems the limit is not valid numeric value.'
187+
),
188+
'ru': 'Кажется, лимит имеет неверное значение.',
189+
}
190+
179191
CATEGORIES_TITLE_UPDATED = {
180192
'default': 'The title successfully updated.',
181193
'ru': 'Название обновлено.',
@@ -249,7 +261,12 @@
249261
'ru': 'Введите название категории.',
250262
}
251263

252-
CATEGORIES_SET_LIMITS = {
264+
CATEGORIES_NO_LIMIT = {
265+
'default': 'no limit',
266+
'ru': 'не установлен',
267+
}
268+
269+
CATEGORIES_SET_LIMIT = {
253270
'default': (
254271
'Please send monthly limit for category <strong>{title}</strong>. '
255272
'Current limit: <strong>{limit}</strong>.'
@@ -313,14 +330,16 @@
313330
'<strong>{category_title}</strong> category of '
314331
'<strong>{book_title}</strong> book. '
315332
'Total amounts added in <strong>{month_label} {year}</stronh> '
316-
'is <strong>{total_amount} {currency}</strong>.'
333+
'is <strong>{total_amount} {currency}</strong>. '
334+
'Monthly limit: <strong>{monthly_limit}</strong>.'
317335
),
318336
'ru': (
319337
'<strong>{amount} {currency}</strong> добавлено в категорию '
320338
'<strong>{category_title}</strong> учетной книги '
321339
'<strong>{book_title}</strong>. '
322340
'Всего за <strong>{month_label} {year}</strong> '
323-
'добавлено <strong>{total_amount} {currency}</strong>.'
341+
'добавлено <strong>{total_amount} {currency}</strong>. '
342+
'Месячный лимит: <strong>{monthly_limit}</strong>.'
324343
),
325344
}
326345

0 commit comments

Comments
 (0)