from __future__ import annotations import datetime from typing import Dict, List, Optional, Set, Union from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator from app.db_sql import redis_lookup_id_random, sql_delete, sql_insert_or_update, sql_select from app.lib_general import log, logging from app.methods.order_cfg_methods import load_order_cfg_obj from app.methods.order_line_methods import get_order_line_rec_list, load_order_line_obj # from app.methods.person_methods import load_person_obj # from app.methods.user_methods import load_user_obj from app.models.order_models import Order_Base from app.models.order_line_models import Order_Line_Base, Order_Line_DB_Base # This should go away later. # from app.models.person_models import Person_Base # from app.models.user_models import User_Base # ### BEGIN ### API Order Methods ### save_order_obj() ### def save_order_obj(order_obj_new:Order_Base, repl_order_line_li:bool=False): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) log.debug(order_obj_new.dict(by_alias=False, exclude_defaults=False, exclude_unset=True)) order_line_obj_li_curr = [] # Initialize to store order_line list if order_obj_new.id_random: log.info(f'An order.id {order_obj_new.id} or order.id_random {order_obj_new.id_random} was included. We can update an existing order.') log.info(f'Get the current order_line list to compare with what was sent...') data = {} data['order_id_random'] = order_obj_new.id_random if order_line_rec_li_curr := sql_select(table_name='v_order_line', data=data, rm_id_random=True, as_list=True): #log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(order_line_rec_li_curr) for order_line_rec in order_line_rec_li_curr: try: order_line_obj = Order_Line_Base(**order_line_rec) log.debug(order_line_obj) except ValidationError as e: log.error(e.json()) order_line_obj_li_curr.append(order_line_obj) else: log.info(f'No order_line records were found') elif order_obj_new.account_id_random and (order_obj_new.person_id_random or order_obj_new.user_id_random): log.info(f'An account.id_random {order_obj_new.account_id_random} was passed. And either a person.id_random {order_obj_new.person_id_random} or user.id_random {order_obj_new.user_id_random} was passed. We can create a new order.') # Because there was not an order ID, assume there are no order lines yet. So no look up. else: log.info('Either an order ID is required to update an order or an account ID along with a person ID or user ID is required to create an order.') return False #log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(order_line_obj_li_curr) if repl_order_line_li: # This will remove any order line list items not sent with the new order information. log.info('Removing any order line list items not sent with the new order information...') for index, order_line_obj_curr in enumerate(order_line_obj_li_curr): log.info(f'Current: order line ID={order_line_obj_curr.id_random} and product ID= {order_line_obj_curr.product_id_random}') matched_product_id = False for order_line_obj_new in order_obj_new.order_line_li: log.debug(f'Checking new: product ID={order_line_obj_new.product_id_random}') if order_line_obj_curr.product_id_random == order_line_obj_new.product_id_random: matched_product_id = True log.debug(f'Matched: product ID={order_line_obj_new.product_id_random}') break else: log.debug(f'No match: product ID={order_line_obj_new.product_id_random}') if not matched_product_id: # Was not found in the new order line list sent log.info(f'Current order line product ID did not match any of the new list. DELETE order line ID {order_line_obj_curr.id_random} with product ID {order_line_obj_curr.product_id_random}') if order_line_del_result := sql_delete(table_name='order_line', record_id_random=order_line_obj_curr.id_random): log.info(f'Deleted record and now pop the current list item {index}...') order_line_obj_li_curr.pop(index) else: log.info(f'Current order line product ID matched. Keeping order line ID {order_line_obj_curr.id_random} with product ID {order_line_obj_curr.product_id_random}') # NOTE: That this current order line item will be updated below. log.debug(order_line_obj_li_curr) #log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.info('Loop through the line list that was sent and compare with what was pulled from the DB') # Loop through the new line list that was sent and compare with the current line list that was pulled from the DB # Only insert if a product ID does not match # Only update if a product ID does match for order_line_obj_new in order_obj_new.order_line_li: log.info(f'New: order line ID={order_line_obj_new.id_random} and product ID= {order_line_obj_new.product_id_random}') matched_product_id = False for index, order_line_obj_curr in enumerate(order_line_obj_li_curr): log.debug(f'Checking current: product ID={order_line_obj_curr.product_id_random}') if order_line_obj_new.product_id_random == order_line_obj_curr.product_id_random: matched_product_id = True log.debug(f'Matched: product ID={order_line_obj_curr.product_id_random}') log.info(f'Updating the current line item with the new line item.') order_line_obj_new.id_random = order_line_obj_curr.id_random order_line_obj_new.id = order_line_obj_curr.id order_line_obj_li_curr[index] = order_line_obj_new break else: log.debug(f'No match: product ID={order_line_obj_curr.product_id_random}') if not matched_product_id: # Was not found in the current order line list that was pulled from the DB log.info(f'New order line product ID did not match any of the current list. Append order line ID {order_line_obj_new.id_random} with product ID {order_line_obj_new.product_id_random}') log.info('Append to current list...') order_line_obj_li_curr.append(order_line_obj_new) # These will be inserted/updated below # Save merged current and new list to the new order object order_obj_new.order_line_li = order_line_obj_li_curr log.debug(order_obj_new) # Final loop through to get the new order totals # Calculate totals order_total_amount:int = 0 order_total_quantity:int = 0 for order_line_obj_new in order_obj_new.order_line_li: order_total_amount += order_line_obj_new.quantity * order_line_obj_new.amount order_total_quantity += order_line_obj_new.quantity order_obj_new.total_bill = order_total_amount # "amount" is used by order_cart and Stripe order_obj_new.total_quantity = order_total_quantity order_obj_new.balance = order_total_amount - order_obj_new.total_paid log.debug(order_obj_new.dict(by_alias=False, exclude_defaults=False, exclude_unset=True, exclude={'order_line_li', 'cfg', 'created_on', 'updated_on'})) order_obj_data = order_obj_new.dict(by_alias=False, exclude_defaults=False, exclude_unset=True, exclude={'order_line_li', 'cfg', 'created_on', 'updated_on'}) # SQL INSERT or UPDATE the order record log.info('SQL INSERT or UPDATE the order record') if order_obj_resp := sql_insert_or_update(data=order_obj_data, table_name='order', rm_id_random=True, id_random_length=8): pass else: return False #log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(order_obj_resp) if isinstance(order_obj_resp, bool) and order_obj_resp: if order_id := order_obj_new.id: pass elif order_id := order_obj_new.id_random: pass elif isinstance(order_obj_resp, int): order_id = order_obj_resp else: return False log.debug(f'Order ID={order_id}') # Loop through the order_line list to SQL INSERT or UPDATE the records log.info('Loop through the order_line list to SQL INSERT or UPDATE the records') for order_line_obj_new in order_obj_new.order_line_li: log.info(f"New order_line: order_line_id_random={order_line_obj_new.id_random}; product_id_random={order_line_obj_new.product_id_random}") log.debug(order_line_obj_new.dict(by_alias=False, exclude_defaults=False, exclude_unset=False, exclude={'order_line_id_random', 'product_type_id', 'product_type', 'created_on', 'updated_on'})) order_line_obj_data = order_line_obj_new.dict(by_alias=False, exclude_defaults=False, exclude_unset=True, exclude={'order_line_id_random', 'product_type_id', 'product_type', 'created_on', 'updated_on'}) # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL try: order_line_obj_db = Order_Line_DB_Base(**order_line_obj_new) log.debug(order_line_obj_db) except ValidationError as e: log.error(e.json()) return False order_line_obj_db_data = order_line_obj_db.dict(by_alias=False, exclude_defaults=False, exclude_unset=True) log.debug(order_line_obj_db_data) order_line_obj_data['order_id'] = order_id if order_line_obj_resp := sql_insert_or_update(sql=None, data=order_line_obj_data, table_name='order_line', rm_id_random=True, id_random_length=8): pass else: return False log.debug(order_line_obj_resp) return order_id # ### END ### API Order Methods ### save_order_obj() ### # ### BEGIN ### API Order Methods ### load_order_obj() ### def load_order_obj( order_id: int|str, limit: int = 1000, by_alias: bool = True, exclude_unset: bool = True, model_as_dict: bool = False, enabled: str = 'enabled', # enabled, disabled, all inc_order_line_list: bool = False, inc_order_cfg: bool = False, inc_person: bool = False, inc_user: bool = False, ): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if order_id := redis_lookup_id_random(record_id_random=order_id, table_name='order'): pass else: return False if order_rec := sql_select(table_name='v_order', record_id=order_id): pass else: return False #log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(order_rec) try: order_obj = Order_Base(**order_rec) log.debug(order_obj) except ValidationError as e: log.error(e.json()) # Updated 2021-06-18 if inc_order_line_list: if order_line_rec_list_result := get_order_line_rec_list( for_obj_type = 'order', for_obj_id = order_id, limit = limit, ): order_line_result_list = [] for order_line_rec in order_line_rec_list_result: order_line_result_list.append( load_order_line_obj( order_line_id = order_line_rec.get('order_line_id', None), limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = model_as_dict, ) ) order_obj.order_line_list = order_line_result_list else: order_obj.order_line_list = [] # Updated 2021-08-07 if inc_order_cfg: # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL if order_cfg_result := load_order_cfg_obj( account_id = order_rec.get('account_id', None), by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = True, ): order_obj.cfg = order_cfg_result else: order_obj.cfg = None log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(order_cfg_result) # Updated 2021-06-22 if inc_person: from app.methods.person_methods import load_person_obj if person_result := load_person_obj( person_id = order_rec.get('person_id', None), limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = model_as_dict, enabled = enabled, ): order_obj.person = person_result else: order_obj.person = None pass # Updated 2021-06-22 if inc_user: from app.methods.user_methods import load_user_obj if user_result := load_user_obj( user_id = order_rec.get('user_id', None), limit = limit, by_alias = by_alias, exclude_unset = exclude_unset, model_as_dict = model_as_dict, enabled = enabled, ): order_obj.user = user_result else: order_obj.user = None pass if model_as_dict: return order_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset) # pylint: disable=no-member else: return order_obj # ### END ### API Order Methods ### load_order_obj() ### # ### BEGIN ### API Order Methods ### get_order_rec_list() ### def get_order_rec_list( for_obj_type: str, for_obj_id: str, limit: int = 1000, enabled: str = 'enabled', # enabled, disabled, all from_datetime: datetime.datetime = None, to_datetime: datetime.datetime = None, status: str = 'complete', # started, in progress, complete, all balance_gt: int = 0, # $0 to $99999 ) -> list|bool: log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if for_obj_id := redis_lookup_id_random(record_id_random=for_obj_id, table_name=for_obj_type): pass else: return False data = {} data[f'{for_obj_type}_id'] = for_obj_id # data['for_obj_type'] = for_obj_type sql_obj_type_id = f'`tbl`.{for_obj_type}_id = :{for_obj_type}_id' allowed_status_li = ['started', 'in progress', 'complete', 'all'] sql_status = '' if status in allowed_status_li: if status == 'status': data['status'] = True sql_status = f'AND `tbl`.status = :status' elif status == 'disabled': data['status'] = False sql_status = f'AND `tbl`.status = :status' elif status == 'all': sql_status = '' else: log.warning('The status value passed is not allowed. Returning None') return None # if enabled in ['enabled', 'disabled', 'all']: # if enabled == 'enabled': # data['enable'] = True # sql_enabled = f'AND `tbl`.enable = :enable' # elif enabled == 'disabled': # data['enable'] = False # sql_enabled = f'AND `tbl`.enable = :enable' # elif enabled == 'all': # sql_enabled = '' sql_enabled = '' if limit: data['limit'] = limit sql_limit = f'LIMIT :limit' else: sql_limit = '' sql = f""" SELECT `tbl`.id AS 'order_id', `tbl`.id_random AS 'order_id_random' FROM `order` AS `tbl` WHERE {sql_obj_type_id} {sql_status} {sql_enabled} ORDER BY `tbl`.created_on DESC, `tbl`.updated_on DESC {sql_limit}; """ if order_rec_li_result := sql_select(data=data, sql=sql, as_list=True): order_rec_li = order_rec_li_result else: order_rec_li = [] log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(order_rec_li_result) return order_rec_li # ### END ### API Order Methods ### get_order_rec_list() ###