def box_new(self, box_number: str, box_type: Union[str, int, BoxType]) -> Box: """ Add a new empty box to the inventory system. If successful it adds an activity record for an empty box and returns the box record just created. Requirements: * Box number is valid, unique, and not previously assigned * Box type is valid Exceptions: 101 - Box number supplied is not in the valid format ('BOXnnnn') 102 - Box number is not unique 102 - Box type is not valid :param box_number: in the form of 'BOXnnnnn' :param box_type: a valid box type code, BoxType record or record id :return: the newly created box record """ # box number validation if not BoxNumber.validate(box_number): raise InvalidValueError(f'101 - Box number of "{box_number}" is ' f'improperly formatted or missing') box_exists_qs = Box.objects.filter(box_number=box_number) if len(box_exists_qs) > 0: raise InvalidActionAttemptedError( f'102 - Creating a new box {box_number} failed because it' f'already exists') # box type validation - either code, id or record if type(box_type) == BoxType: self.box_type = box_type elif type(box_type) == int: try: self.box_type = BoxType.objects.get(pk=box_type) except BoxType.DoesNotExist: raise InvalidValueError( f'102 - Box type id of "{box_type}" is invalid') else: try: self.box_type = BoxType.objects.get(box_type_code=box_type) except BoxType.DoesNotExist: raise InvalidValueError( f'102 - Box type code of "{box_type}" is invalid') self._new_box(box_number, self.box_type) return self.box
def validate_exp_month_start_end( exp_month_start: Optional[int], exp_month_end: Optional[int] ) -> bool: """ Validate the start and end month, if given. :param exp_month_start: number 1-12 (integer or string) :param exp_month_end: number 1-12 (integer or string) :return: """ exp_month_start = 0 if exp_month_start is None else exp_month_start exp_month_end = 0 if exp_month_end is None else exp_month_end if exp_month_start == 0 and exp_month_end == 0 : return True error_msg = ( "If Exp {} month is specified, Exp {} month must also be specified" ) if exp_month_start != 0 and exp_month_end == 0: raise InvalidValueError(error_msg.format('start', 'end')) if exp_month_end != 0 and exp_month_start == 0: raise InvalidValueError(error_msg.format('end', 'start')) try: exp_month_start = int(exp_month_start) except (TypeError, ValueError): raise InvalidValueError( "Exp month start {} is not an integer".format( repr(exp_month_start), ) ) try: exp_month_end = int(exp_month_end) except (TypeError, ValueError): raise InvalidValueError( "Exp month end {} is not an integer".format( repr(exp_month_end) ) ) if exp_month_end < exp_month_start: raise InvalidValueError( 'Exp month end must be later than or equal to Exp month start' ) return True
def box_move(self, box: Union[Box, int], location: Union[Location, int]): """ Move an individual box in the inventory. The activity record for this box will be changed to show the new location. The old location will be dropped from the activity record. If successful, the box record will be returned. Requirements: * Box is filled * Location is valid Exceptions: 121 - Box not in system 122 - Cannot move an empty box 123 - Location is not valid :param box: :param location: :return: """ if type(box) == Box: self.box = box else: try: self.box = Box.objects.get(pk=box).select_related('box_type') except Box.DoesNotExist: raise InvalidActionAttemptedError( f'121 - Attempting to move a box that does not exist. ID ' f'given was "{box}"') if not self.box.product: raise InvalidActionAttemptedError( f'122 - Attempting to move an empty box') if type(location) == Location: self.location = location else: try: self.location = Location.objects.get(pk=location) except Location.DoesNotExist: raise InvalidValueError( f'123 - Location with ID "{location}" not found') self._move_box() return self.box
def pallet_finish(self, pallet: Union[Pallet, int, str]): """ Finish the processing of a pallet of boxes into inventory. Each box of the pallet will be processed. Nothing will be returned. Note - a pallet is still considered valid even if there are no boxes associated with it. Requirements: * A valid pallet record, pallet name, or ID * A pallet status indicating if the boxes have just been filled or are being moved to a new location Exception: 161 - An invalid pallet ID was passed in 162 - An invalid pallet name was passed in 166 - The pallet has an invalid location :param pallet: :return: """ if type(pallet) == Pallet: pallet_rec = pallet elif type(pallet) == int: try: pallet_rec = Pallet.objects.get(pk=pallet) except Pallet.DoesNotExist: raise InvalidValueError( f'161 - A pallet with ID: {pallet} does not exist') else: try: pallet_rec = Pallet.objects.get(name=pallet) except Pallet.DoesNotExist: raise InvalidValueError( f'162 - A pallet with the name "{pallet}" does not ' f'currently exist') try: location = Location.objects.get(pk=pallet_rec.location.id) except Location.DoesNotExist: raise InvalidValueError( f'166 - The location of "{pallet_rec.location}" does not ' f'exist') # TODO Mar 19 2020 travis - temporary fix pallet_status = pallet.pallet_status if pallet_status is None or pallet_status.strip() == '': pallet_status = Pallet.FILL pallet_boxes = PalletBox.objects.filter(pallet=pallet_rec) # transfer info and delete the pallet and its boxes in one trans with transaction.atomic(): if pallet_status == Pallet.FILL: # transfer the information to the real boxes for pallet_box in pallet_boxes: box = pallet_box.box product = pallet_box.product exp_year = pallet_box.exp_year exp_mo_start = pallet_box.exp_month_start exp_mo_end = pallet_box.exp_month_end self.box_fill( box=box, location=location, product=product, exp_year=exp_year, exp_mo_start=exp_mo_start, exp_mo_end=exp_mo_end, ) else: # move or merge the boxes to the new location for pallet_box in pallet_boxes: box = pallet_box.box self.box_move( box=box, location=location, ) # delete the pallet boxes for this pallet en mass pallet_boxes.delete() # now delete the pallet itself pallet_rec.delete() return
def box_fill(self, *, box: Union[Box, int], location: Union[Location, int], product: Union[Product, int], exp_year: int, exp_mo_start: int = 0, exp_mo_end: int = 0): """ Fill an individual box with product and add to the inventory. If the box is not empty, an activity record will empty the box of its previous contents and a new activity record will note the new contents profiled. If successful, it will return the box just filled. Requirements: * Box record has not been modified * All required fields are valid * Optional month start and end, if specified, bracket one or more months Exceptions: 111 - Attempting to fill a box that does not exist 112 - location supplied is not valid 113 - the product supplied is not valid 114 - the expiration year, start month, and/or end month are not valid or are out of range :param box: Box record or id of a box already in the system :param location: Target location record or ID :param product: Target product record or ID :param exp_year: year (current year +/- 10) :param exp_mo_start: 1 - 12 if specified - usually beginning quarter :param exp_mo_end: 1-12 if specified - usually ending quarter :return: box record after modifications """ if type(box) == Box: self.box = box else: try: self.box = Box.objects.select_related('box_type').get(pk=box) except Box.DoesNotExist: raise InvalidActionAttemptedError( f'111 - Attempting to fill a box that does not exist. ID ' f'given was "{box}"') if type(location) == Location: self.location = location else: try: self.location = Location.objects.get(pk=location) except Location.DoesNotExist: raise InvalidValueError( f'112 - Location with ID "{location}" not found') if type(product) == Product: self.product = product else: try: self.product = Product.objects.get(pk=product) except Product.DoesNotExist: raise InvalidValueError( f'113 - Product with ID "{product}" not found') # presume the date information is true until proven otherwise expiration_info_valid = True years_ahead_list = Constraints.get_values( Constraints.FUTURE_EXP_YEAR_LIMIT) years_ahead = years_ahead_list[0] future_exp_year_limit = CURRENT_YEAR + years_ahead if exp_year < CURRENT_YEAR or exp_year > future_exp_year_limit: expiration_info_valid = False else: self.exp_year = exp_year # first presume both months are zero or none self.exp_mo_end = 0 # both valid months or zero or null if (exp_mo_start is None) or exp_mo_start == 0: self.exp_mo_start = 0 elif 1 <= exp_mo_start <= 12: self.exp_mo_start = exp_mo_start else: expiration_info_valid = False if (exp_mo_end is None) or exp_mo_end == 0: self.exp_mo_end = 0 elif 1 <= exp_mo_end <= 12: self.exp_mo_end = exp_mo_end else: expiration_info_valid = False # end must be greater than or equal to start if self.exp_mo_end < self.exp_mo_start: expiration_info_valid = False # did it pass the gauntlet? if not expiration_info_valid: raise InvalidValueError( f'114 - Expiration date information of {exp_mo_start} -' f' {exp_mo_end} - {exp_year} was not valid') self._fill_box() return self.box