forked from Support/sp-hydra-veil-gui
Improved error handling for billing conflicts of individual profiles at the same time. Overall flow is stable in this version
This commit is contained in:
parent
39925ad546
commit
ea12aecf05
8 changed files with 117 additions and 72 deletions
|
|
@ -1,19 +1,21 @@
|
||||||
# core
|
# core
|
||||||
from core.controllers.ConfigurationController import ConfigurationController
|
from core.controllers.ConfigurationController import ConfigurationController
|
||||||
from core.services.prepare_tickets.ticket_tracker import get_a_random_ticket
|
# from core.services.prepare_tickets.ticket_tracker import get_a_random_ticket
|
||||||
from core.services.profiles.profile_connections import enable_profile #, disable_profile
|
from core.services.profiles.profile_connections import enable_profile #, disable_profile
|
||||||
from core.controllers.ProfileController import ProfileController
|
from core.controllers.ProfileController import ProfileController
|
||||||
from core.models.Configuration import Configuration, ConnectionChoice
|
from core.models.Configuration import Configuration, ConnectionChoice
|
||||||
from core.services.subscriptions.subscriptions_by_protocol import get_profile_subscription, start_new_payment_cycle, wipe_subscription
|
from core.services.subscriptions.subscriptions_by_protocol import get_profile_subscription, start_new_payment_cycle, wipe_subscription, evaluate_repeat_cycle
|
||||||
from core.utils.check_if_time_lapsed import check_if_time_lapsed
|
from core.utils.check_if_time_lapsed import check_if_time_lapsed
|
||||||
from core.models.enum_models import PaymentCategory
|
from core.models.enum_models import PaymentCategory
|
||||||
from core.utils.type_checker import type_checker
|
from core.utils.type_checker import type_checker
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
from core.errors.logger import logger
|
from core.errors.logger import logger
|
||||||
from core.errors.translate_error import translate_error, get_ignored_exceptions
|
from core.errors.translate_error import translate_error, get_ignored_exceptions
|
||||||
|
from core.services.payment_phase.payment_state import OverallPaymentCycle
|
||||||
|
|
||||||
# gui
|
# gui
|
||||||
from gui.v2.actions.ticket_actions import get_ticket_data, use_a_ticket
|
from gui.v2.actions.payment_actions import wipe_expired_subscription
|
||||||
|
from gui.v2.actions.ticket_actions import dispatch_ticketing
|
||||||
from gui.v2.infrastructure.action_registry import register_action
|
from gui.v2.infrastructure.action_registry import register_action
|
||||||
from gui.v2.infrastructure.take_plumbing_action import take_action
|
from gui.v2.infrastructure.take_plumbing_action import take_action
|
||||||
from gui.v2.infrastructure.screen_size import get_max_screensize
|
from gui.v2.infrastructure.screen_size import get_max_screensize
|
||||||
|
|
@ -25,6 +27,7 @@ from gui.v2.actions.error_handlers import navigate_to_new_sub_page
|
||||||
|
|
||||||
# generic
|
# generic
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
@type_checker
|
@type_checker
|
||||||
def get_profile(profile_id: int):
|
def get_profile(profile_id: int):
|
||||||
|
|
@ -35,56 +38,21 @@ def get_profile(profile_id: int):
|
||||||
return Result(valid=True, data=profile)
|
return Result(valid=True, data=profile)
|
||||||
|
|
||||||
|
|
||||||
@register_action(ResultError.NO_TICKET)
|
def dispatch_individual(profile_id: int, ui_caller: Callable) -> Result:
|
||||||
def start_and_navigate_to_ticketing():
|
payment_cycle = OverallPaymentCycle()
|
||||||
"""Starts a new ticketing cycle. (assumes the user opted in)."""
|
|
||||||
start_result = start_new_payment_cycle(category=PaymentCategory.TICKET)
|
print(f"payment_cycle is {payment_cycle}")
|
||||||
if start_result.valid:
|
|
||||||
logger.info("creating a new payment cycle was valid, it worked.")
|
if payment_cycle.active:
|
||||||
return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="plan_picker")
|
same_result = evaluate_repeat_cycle(payment_cycle=payment_cycle, profile_id=profile_id)
|
||||||
|
if not same_result.valid:
|
||||||
|
return same_result
|
||||||
else:
|
else:
|
||||||
logger.info(f"Returning the error of start_result {start_result}")
|
error_msg = "There's an error in the dispatch_individual function"
|
||||||
return start_result
|
logger.error(f"This shouldn't be going off. {error_msg}")
|
||||||
|
return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="home", message=error_msg)
|
||||||
|
|
||||||
@type_checker
|
|
||||||
def random_ticket_dispatch(profile_id: int, passed_ui_caller: Callable) -> Result:
|
|
||||||
"""
|
|
||||||
User explicitly wants a random ticket.
|
|
||||||
The reason this is in GUI repo, is because use_a_ticket is.
|
|
||||||
"""
|
|
||||||
|
|
||||||
random_ticket_result = get_a_random_ticket()
|
|
||||||
|
|
||||||
if not random_ticket_result.valid:
|
|
||||||
return random_ticket_result
|
|
||||||
|
|
||||||
which_ticket = random_ticket_result.data
|
|
||||||
|
|
||||||
# this is a redundant sanity check:
|
|
||||||
if not which_ticket:
|
|
||||||
Result(valid=False, error_type=ResultError.NO_TICKET, message="No tickets left. Would you like to get some?")
|
|
||||||
|
|
||||||
# calls GUI repo function:
|
|
||||||
return use_a_ticket(profile_id=profile_id, which_ticket=which_ticket, ui_caller=passed_ui_caller)
|
|
||||||
|
|
||||||
@type_checker
|
|
||||||
def dispatch_ticketing(profile_id: int, passed_ui_caller: Callable) -> Result:
|
|
||||||
"""Evaluate if a random ticket will be used."""
|
|
||||||
|
|
||||||
use_random_setting = ConfigurationController.use_random()
|
|
||||||
|
|
||||||
if use_random_setting:
|
|
||||||
return random_ticket_dispatch(profile_id=profile_id, passed_ui_caller=passed_ui_caller)
|
|
||||||
|
|
||||||
# User does NOT want an automated selection, so they can pick from their tickets:
|
|
||||||
do_they_have_tickets = get_ticket_data()
|
|
||||||
|
|
||||||
if do_they_have_tickets.valid:
|
|
||||||
return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="ticket_or_billing_choice")
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# user can decide if they want to setup tickets or do an individual profile:
|
# Starting fresh. Pick a billing type:
|
||||||
return navigate_to_new_sub_page()
|
return navigate_to_new_sub_page()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -103,8 +71,7 @@ def filter_subscription(profile_id: int, passed_ui_caller: Callable) -> Result:
|
||||||
# already opted into ticketing:
|
# already opted into ticketing:
|
||||||
return dispatch_ticketing(profile_id=profile_id, passed_ui_caller=passed_ui_caller)
|
return dispatch_ticketing(profile_id=profile_id, passed_ui_caller=passed_ui_caller)
|
||||||
else:
|
else:
|
||||||
# Starting fresh. Pick a billing type:
|
return dispatch_individual(profile_id=profile_id, ui_caller=passed_ui_caller)
|
||||||
return navigate_to_new_sub_page()
|
|
||||||
|
|
||||||
#-----------------------------
|
#-----------------------------
|
||||||
# Pending Subscription
|
# Pending Subscription
|
||||||
|
|
@ -128,7 +95,8 @@ def filter_subscription(profile_id: int, passed_ui_caller: Callable) -> Result:
|
||||||
|
|
||||||
expired = check_if_time_lapsed(expiration_date)
|
expired = check_if_time_lapsed(expiration_date)
|
||||||
if expired:
|
if expired:
|
||||||
return Result(valid=False, error_type=ResultError.EXPIRED_SUB, message="This subscription is expired, would you like to wipe it and get a new one?")
|
function_choice = partial(wipe_subscription, profile_id=profile_id)
|
||||||
|
return Result(valid=False, function_choice=function_choice, error_type=ResultError.EXPIRED_SUB, message="This subscription is expired, would you like to wipe it and get a new one?")
|
||||||
|
|
||||||
#-----------------------------
|
#-----------------------------
|
||||||
# Proceed with Valid Sub
|
# Proceed with Valid Sub
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,22 @@ def start_polling(billing_code: str, profile_id: int, ui_caller: Callable) -> st
|
||||||
check_paid_timer.start_polling()
|
check_paid_timer.start_polling()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@register_action(ResultError.BILLING_CONFLICT)
|
||||||
|
def stop_current() -> Result:
|
||||||
|
wiped_result = wipe_billing_cycle()
|
||||||
|
|
||||||
|
logger.info("Stopping current!")
|
||||||
|
|
||||||
|
# if not wiped_result.valid:
|
||||||
|
# return wiped_result
|
||||||
|
|
||||||
|
# new_cycle_result = start_new_payment_cycle(category=PaymentCategory.INDIVIDUAL, profile_id=profile_id)
|
||||||
|
|
||||||
|
# if not new_cycle_result.valid:
|
||||||
|
# return new_cycle_result
|
||||||
|
|
||||||
|
return Result(valid=True, error_type=ResultError.NAVIGATE_TO, navigate_to="menu")
|
||||||
|
|
||||||
|
|
||||||
@register_action(ResultError.ACTIVE_BILLING)
|
@register_action(ResultError.ACTIVE_BILLING)
|
||||||
def stop_payment_timer() -> Result:
|
def stop_payment_timer() -> Result:
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
# core
|
# core
|
||||||
|
from core.controllers.ConfigurationController import ConfigurationController
|
||||||
from core.controllers.tickets.UseTicketController import use_ticket
|
from core.controllers.tickets.UseTicketController import use_ticket
|
||||||
|
from core.services.prepare_tickets.ticket_tracker import get_a_random_ticket
|
||||||
from core.models.BaseProfile import BaseProfile as Profile
|
from core.models.BaseProfile import BaseProfile as Profile
|
||||||
from core.controllers.ProfileController import ProfileController
|
from core.controllers.ProfileController import ProfileController
|
||||||
from core.controllers.tickets.TicketPayController import initiate_payment
|
# from core.controllers.tickets.TicketPayController import initiate_payment
|
||||||
from core.services.subscriptions.subscriptions_by_protocol import start_new_payment_cycle, save_subscription, get_ticket_invoice
|
from core.services.subscriptions.subscriptions_by_protocol import start_new_payment_cycle, save_subscription, get_ticket_invoice
|
||||||
from core.services.payment_phase.ticket_cycle import wipe_ticket_cycle
|
from core.services.payment_phase.ticket_cycle import wipe_ticket_cycle
|
||||||
from core.services.payment_phase.payment_state import OverallPaymentCycle
|
# from core.services.payment_phase.payment_state import OverallPaymentCycle
|
||||||
from core.models.enum_models import PaymentCategory
|
from core.models.enum_models import PaymentCategory
|
||||||
from core.services.prepare_tickets.ticket_tracker import get_all_unused_tickets
|
from core.services.prepare_tickets.ticket_tracker import get_all_unused_tickets
|
||||||
|
from core.utils.type_checker import type_checker
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
from core.errors.logger import logger
|
from core.errors.logger import logger
|
||||||
|
|
||||||
|
|
@ -15,11 +18,66 @@ from core.errors.logger import logger
|
||||||
from gui.v2.infrastructure.action_registry import register_action
|
from gui.v2.infrastructure.action_registry import register_action
|
||||||
from gui.v2.infrastructure.take_plumbing_action import take_action
|
from gui.v2.infrastructure.take_plumbing_action import take_action
|
||||||
from gui.v2.infrastructure.setup_observers import connection_observer, ticket_observer
|
from gui.v2.infrastructure.setup_observers import connection_observer, ticket_observer
|
||||||
|
from gui.v2.actions.error_handlers import navigate_to_new_sub_page
|
||||||
|
|
||||||
# generic:
|
# generic:
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
|
@register_action(ResultError.NO_TICKET)
|
||||||
|
def start_and_navigate_to_ticketing():
|
||||||
|
"""Starts a new ticketing cycle. (assumes the user opted in)."""
|
||||||
|
start_result = start_new_payment_cycle(category=PaymentCategory.TICKET)
|
||||||
|
if start_result.valid:
|
||||||
|
logger.info("creating a new payment cycle was valid, it worked.")
|
||||||
|
return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="plan_picker")
|
||||||
|
else:
|
||||||
|
logger.info(f"Returning the error of start_result {start_result}")
|
||||||
|
return start_result
|
||||||
|
|
||||||
|
|
||||||
|
@type_checker
|
||||||
|
def random_ticket_dispatch(profile_id: int, passed_ui_caller: Callable) -> Result:
|
||||||
|
"""
|
||||||
|
User explicitly wants a random ticket.
|
||||||
|
The reason this is in GUI repo, is because use_a_ticket is.
|
||||||
|
"""
|
||||||
|
|
||||||
|
random_ticket_result = get_a_random_ticket()
|
||||||
|
|
||||||
|
if not random_ticket_result.valid:
|
||||||
|
return random_ticket_result
|
||||||
|
|
||||||
|
which_ticket = random_ticket_result.data
|
||||||
|
|
||||||
|
# this is a redundant sanity check:
|
||||||
|
if not which_ticket:
|
||||||
|
Result(valid=False, error_type=ResultError.NO_TICKET, message="No tickets left. Would you like to get some?")
|
||||||
|
|
||||||
|
# calls GUI repo function:
|
||||||
|
return use_a_ticket(profile_id=profile_id, which_ticket=which_ticket, ui_caller=passed_ui_caller)
|
||||||
|
|
||||||
|
|
||||||
|
@type_checker
|
||||||
|
def dispatch_ticketing(profile_id: int, passed_ui_caller: Callable) -> Result:
|
||||||
|
"""Evaluate if a random ticket will be used."""
|
||||||
|
|
||||||
|
use_random_setting = ConfigurationController.use_random()
|
||||||
|
|
||||||
|
if use_random_setting:
|
||||||
|
return random_ticket_dispatch(profile_id=profile_id, passed_ui_caller=passed_ui_caller)
|
||||||
|
|
||||||
|
# User does NOT want an automated selection, so they can pick from their tickets:
|
||||||
|
do_they_have_tickets = get_ticket_data()
|
||||||
|
|
||||||
|
if do_they_have_tickets.valid:
|
||||||
|
return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="ticket_or_billing_choice")
|
||||||
|
|
||||||
|
else:
|
||||||
|
# user can decide if they want to setup tickets or do an individual profile:
|
||||||
|
return navigate_to_new_sub_page()
|
||||||
|
|
||||||
|
|
||||||
def start_ticket(ui_caller: Callable) -> Result:
|
def start_ticket(ui_caller: Callable) -> Result:
|
||||||
logger.info("started ticketing billing state")
|
logger.info("started ticketing billing state")
|
||||||
return take_action(
|
return take_action(
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ class Navigator(QObject):
|
||||||
self._preload_dispatched += 1
|
self._preload_dispatched += 1
|
||||||
idx = self._preload_dispatched
|
idx = self._preload_dispatched
|
||||||
name = self._preload_queue.pop(0)
|
name = self._preload_queue.pop(0)
|
||||||
logger.debug(f"[preload] ---- {idx}/{self._preload_total} ----")
|
# logger.debug(f"[preload] ---- {idx}/{self._preload_total} ----")
|
||||||
if name in ASYNC_PREPARE:
|
if name in ASYNC_PREPARE:
|
||||||
self._dispatch_async_preload(name)
|
self._dispatch_async_preload(name)
|
||||||
else:
|
else:
|
||||||
|
|
@ -220,11 +220,12 @@ class Navigator(QObject):
|
||||||
self._emit_preload_summary()
|
self._emit_preload_summary()
|
||||||
|
|
||||||
def _emit_preload_summary(self):
|
def _emit_preload_summary(self):
|
||||||
total_s = time.perf_counter() - self._preload_t_start
|
pass
|
||||||
logger.debug(f"[preload] ============================================================")
|
# total_s = time.perf_counter() - self._preload_t_start
|
||||||
logger.debug(f"[preload] complete: {self._preload_done} loaded, "
|
# logger.debug(f"[preload] ============================================================")
|
||||||
f"{self._preload_failed} failed, "
|
# logger.debug(f"[preload] complete: {self._preload_done} loaded, "
|
||||||
f"{self._preload_skipped_runtime} skipped (user beat us to it) "
|
# f"{self._preload_failed} failed, "
|
||||||
f"in {total_s:.2f}s")
|
# f"{self._preload_skipped_runtime} skipped (user beat us to it) "
|
||||||
logger.debug(f"[preload] cached pages now: {sorted(self._instances.keys())}")
|
# f"in {total_s:.2f}s")
|
||||||
logger.debug(f"[preload] ============================================================")
|
# logger.debug(f"[preload] cached pages now: {sorted(self._instances.keys())}")
|
||||||
|
# logger.debug(f"[preload] ============================================================")
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class CurrencySelectionPage(Page):
|
||||||
|
|
||||||
# get the singleton (this was created on a previous page)
|
# get the singleton (this was created on a previous page)
|
||||||
print("doing the new method")
|
print("doing the new method")
|
||||||
data_result = get_current_billing_data()
|
data_result = get_current_billing_data(profile_id=profile_id)
|
||||||
|
|
||||||
if not data_result.valid:
|
if not data_result.valid:
|
||||||
message = data_result.message
|
message = data_result.message
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ class DurationSelectionPage(Page):
|
||||||
plan_id = self.dict_of_choices[current_choice]
|
plan_id = self.dict_of_choices[current_choice]
|
||||||
|
|
||||||
# save to singleton across pages:
|
# save to singleton across pages:
|
||||||
data_result = get_current_billing_data(category=PaymentCategory.INDIVIDUAL)
|
profile_id = int(self.update_status.current_profile_id)
|
||||||
|
data_result = get_current_billing_data(category=PaymentCategory.INDIVIDUAL, profile_id=profile_id)
|
||||||
|
|
||||||
if not data_result.valid:
|
if not data_result.valid:
|
||||||
message = data_result.message
|
message = data_result.message
|
||||||
|
|
|
||||||
|
|
@ -759,11 +759,12 @@ class MenuPage(Page):
|
||||||
self.update_action_button_visuals(profile_id)
|
self.update_action_button_visuals(profile_id)
|
||||||
|
|
||||||
@mainthread
|
@mainthread
|
||||||
def turn_off_session_visuals(self, profile_id: int):
|
def turn_off_session_visuals(self, profile_id: int, surpress_output: Optional[bool] = False):
|
||||||
"""
|
"""
|
||||||
Flip off the Visual buttons for a given profile.
|
Flip off the Visual buttons for a given profile.
|
||||||
And re-enable the edit button if its currently selected.
|
And re-enable the edit button if its currently selected.
|
||||||
"""
|
"""
|
||||||
|
if not surpress_output:
|
||||||
print(f"Stopping Session {profile_id}!")
|
print(f"Stopping Session {profile_id}!")
|
||||||
# If you want the border to stay when off, set is_active=True
|
# If you want the border to stay when off, set is_active=True
|
||||||
# If you want the border to disappear when off, set is_active=False
|
# If you want the border to disappear when off, set is_active=False
|
||||||
|
|
@ -853,4 +854,4 @@ class MenuPage(Page):
|
||||||
else:
|
else:
|
||||||
# DEFAULT STARTING PLACE:
|
# DEFAULT STARTING PLACE:
|
||||||
# If it's not system-wide and not a session, it must be off.
|
# If it's not system-wide and not a session, it must be off.
|
||||||
self.turn_off_session_visuals(pid)
|
self.turn_off_session_visuals(profile_id=pid, surpress_output=True)
|
||||||
|
|
|
||||||
|
|
@ -415,7 +415,7 @@ class PaymentDetailsPage(Page):
|
||||||
)
|
)
|
||||||
|
|
||||||
# get the singleton (this was created on a previous page)
|
# get the singleton (this was created on a previous page)
|
||||||
data_result = get_current_billing_data()
|
data_result = get_current_billing_data(profile_id=self.profile_id)
|
||||||
|
|
||||||
if not data_result.valid:
|
if not data_result.valid:
|
||||||
message = data_result.message
|
message = data_result.message
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue