def analyse_orders(data_set: dict, sku_id: str, lead_time: Decimal, unit_cost: Decimal, reorder_cost: Decimal, z_value: Decimal, retail_price: Decimal, quantity_on_hand: Decimal, currency: str = 'USD') -> dict: """Analyse orders data for one sku using a dictionary. Analyses orders data for a single sku using the values in the data_set dict. Args: data_set (dict): The orders data for a specified period. sku_id (str): The unique id of the sku. lead_time (Decimal): The average lead-time for the sku over the period represented by the data, in the same unit. unit_cost (Decimal): The unit cost of the sku to the organisation. reorder_cost (Decimal): The cost to place a reorder. This is usually the cost of the operation divided by number of purchase orders placed in the previous period. z_value (Decimal): The service level required to calculate the safety stock. retail_price (Decimal): The retail or standard price of the sku. quantity_on_hand (Decimal): The quantity currently on hand as of analysis or retrieving data set. currency (str): Currency of source raw data. Returns: dict: The summary of the analysis. `{'ABC_XYZ_Classification': 'AX', 'reorder_quantity': '258', 'revenue': '2090910.44', 'average_order': '539', 'reorder_level': '813', 'economic_order_quantity': '277', 'sku': 'RR381-33', 'demand_variability': '0.052', 'economic_order_variable_cost': '29557.61', 'standard_deviation': '28', 'safety_stock': '51'}` Raises: ValueError: Dataset too small. Please use a minimum of 3 entries. Examples: >>> from supplychainpy.model_inventory import analyse_orders >>> from supplychainpy.sample_data.config import ABS_FILE_PATH >>> from decimal import Decimal >>> yearly_demand = {'jan': 75, 'feb': 75, 'mar': 75, 'apr': 75, 'may': 75, 'jun': 75, 'jul': 25, ... 'aug': 25, 'sep': 25, 'oct': 25, 'nov': 25, 'dec': 25} >>> >>> summary = analyse_orders(yearly_demand, ... sku_id='RX983-90', ... lead_time=Decimal(3), ... unit_cost=Decimal(50.99), ... reorder_cost=Decimal(400), ... z_value=Decimal(1.28), ... retail_price=Decimal(600), ... quantity_on_hand=Decimal(390)) """ if len(data_set) > 2: warnings.warn('The \'analyse_orders\' function has been deprecated. Please use the \'analyse\' function', DeprecationWarning) d = analyse_uncertain_demand.UncertainDemand(orders=data_set, sku=sku_id, lead_time=lead_time, unit_cost=unit_cost, reorder_cost=reorder_cost, z_value=z_value, retail_price=retail_price, quantity_on_hand=quantity_on_hand, currency=currency) else: raise ValueError('Data set too small. Please use a minimum of 3 entries.') return d.orders_summary_simple()
def test_order_constraint(self): """Test the constraint for performing the calculation with less than five data points""" orders_placed = [25, 25, 25] with self.assertRaises(Exception): analyse_uncertain_demand.UncertainDemand(orders=orders_placed, sku='Rx493-90', lead_time=Decimal(4), unit_cost=Decimal(40), reorder_cost=Decimal(400), retail_price=Decimal(600), currency='USD')
def _analyse_orders(orders: dict, sku_id: str, lead_time: Decimal, unit_cost: Decimal, reorder_cost: Decimal, z_value: Decimal, retail_price: Decimal, quantity_on_hand: Decimal, currency: str, total_orders: float, backlog: Decimal) -> UncertainDemand: """ Performs several types of common inventory analysis on the raw demand data. Including safety stock, reorder levels. Args: orders (dict): sku_id (str): lead_time (Decimal): unit_cost (Decimal): reorder_cost (Decimal): z_value (Decimal): retail_price (Decimal): quantity_on_hand (Decimal): currency (str): total_orders (float): Returns: UncertainDemand: UncertainDemand objects. """ analysed_orders = analyse_uncertain_demand.UncertainDemand( orders=orders, sku=sku_id, lead_time=lead_time, unit_cost=unit_cost, reorder_cost=Decimal(reorder_cost), z_value=Decimal(z_value), retail_price=retail_price, quantity_on_hand=quantity_on_hand, currency=currency, backlog=backlog) average_orders = analysed_orders.average_orders reorder_quantity = analysed_orders.fixed_order_quantity eoq = economic_order_quantity.EconomicOrderQuantity( total_orders=float(total_orders), reorder_quantity=float(reorder_quantity), holding_cost=float(0.25), reorder_cost=float(reorder_cost), average_orders=average_orders, unit_cost=float(unit_cost)) analysed_orders.economic_order_qty = eoq.economic_order_quantity analysed_orders.economic_order_variable_cost = eoq.minimum_variable_cost return analysed_orders
def setUp(self): self._z_value = Decimal(1.28) self._lead_time = Decimal(4) self._holding_cost_percentge = Decimal(0.25) self._retail_price = Decimal(5000) self._unit_cost = Decimal(55) self._reorder_cost = Decimal(450) self._quantity_on_hand = 1000 self._backlog = 0 self._data_set = { 'jan': 25, 'feb': 25, 'mar': 25, 'apr': 25, 'may': 25, 'jun': 25, 'jul': 75, 'aug': 75, 'sep': 75, 'oct': 75, 'nov': 75, 'dec': 75 } self._orders_analysis = model_inventory.analyse( file_path=ABS_FILE_PATH['COMPLETE_CSV_SM'], z_value=Decimal(1.28), reorder_cost=Decimal(5000), file_type="csv", length=12, currency='USD') self._uncertain_demand = analyse_uncertain_demand.UncertainDemand( self._data_set, sku='Rx493-90', lead_time=self._lead_time, reorder_cost=self._reorder_cost, z_value=self._z_value, holding_cost=self._holding_cost_percentge, retail_price=self._retail_price, unit_cost=self._unit_cost, currency='USD', quantity_on_hand=self._quantity_on_hand, backlog=0)
def analyse_orders_abcxyz_from_file(file_path: str, z_value: Decimal, reorder_cost: Decimal, file_type: str = FileFormats.text.name, period: str = "month", length: int = 12, currency: str = 'USD') -> list: """ Analyse orders data from file and returns ABCXYZ analysis Analyses orders data for a single sku, using the values from a file arranged in columns.The data should be arranged in two columns, 1 for the period and the other for the corresponding data-point. Args: file_path (str): The path to the file containing two columns of data, 1 period and 1 data-point for 1 sku. reorder_cost (Decimal): The average lead-time for the sku over the period represented by the data, in the same unit. length (int): The number of periods in the data-ser referenced from the second column of the row onwards. reorder_cost (Decimal): The cost to place a reorder. This is usually the cost of the operation divided by number of purchase orders placed in the previous period. z_value (Decimal): The service level required to calculate the safety stock file_type (str): Type of 'file csv' or 'text' period (str): The period of time the data points are bucketed into. currency (str): Returns: list: An AbcXyz class object is returned. Raises: Exception: Incorrect file type specified. Please specify 'csv' or 'text' for the file_type parameter. Exception: Unspecified file type, Please specify 'csv' or 'text' for file_type parameter. Examples: >>> from decimal import Decimal >>> from supplychainpy.model_inventory import analyse_orders_from_file_col >>> from supplychainpy.sample_data.config import ABS_FILE_PATH >>> abc = analyse_orders_abcxyz_from_file(file_path=ABS_FILE_PATH['COMPLETE_CSV_SM'], ... z_value=Decimal(1.28), ... reorder_cost=Decimal(5000), ... file_type="csv") """ warnings.warn( 'Analyse orders function has been deprecated. Please use the analyse function', DeprecationWarning) analysed_orders_collection = [] item_list = {} if check_extension(file_path=file_path, file_type=file_type): if file_type == FileFormats.text.name: with open(file_path, 'r') as raw_data: item_list = (_data_cleansing.clean_orders_data_row( raw_data, length)) elif file_type == FileFormats.csv.name: with open(file_path) as raw_data: item_list = _data_cleansing.clean_orders_data_row_csv( raw_data, length=length) else: incorrect_file = "Incorrect file type specified. Please specify 'csv' or 'text' for the file_type parameter." raise Exception(incorrect_file) for sku in item_list: orders = {} sku_id, unit_cost, lead_time, retail_price, quantity_on_hand = sku.get( "sku_id", "UNKNOWN_SKU"), sku.get("unit_cost"), sku.get( "lead_time"), sku.get("retail_price"), sku.get( "quantity_on_hand") orders['demand'] = sku.get("demand") total_orders = 0 if quantity_on_hand is None: quantity_on_hand = 0.0 for order in orders['demand']: total_orders += int(order) analysed_orders = analyse_uncertain_demand.UncertainDemand( orders=orders, sku=sku_id, lead_time=lead_time, unit_cost=unit_cost, reorder_cost=Decimal(reorder_cost), z_value=Decimal(z_value), retail_price=retail_price, quantity_on_hand=quantity_on_hand, currency=currency) average_orders = analysed_orders.average_orders reorder_quantity = analysed_orders.fixed_order_quantity eoq = economic_order_quantity.EconomicOrderQuantity( total_orders=float(total_orders), reorder_quantity=float(reorder_quantity), holding_cost=float(0.25), reorder_cost=float(reorder_cost), average_orders=average_orders, unit_cost=float(unit_cost)) analysed_orders.economic_order_qty = eoq.economic_order_quantity analysed_orders.economic_order_variable_cost = eoq.minimum_variable_cost analysed_orders_collection.append(analysed_orders) AbcXyz(analysed_orders_collection) return analysed_orders_collection
def analyse(currency: str, z_value: Decimal = 1.28, reorder_cost: Decimal = 10, interval_length: int = 12, interval_type: str = 'month', **kwargs): """ Performs several types of common inventory analysis on the raw demand data. Including safety stock, reorder levels. Args: currency (Decimal): Currency the raw data is stored in. z_value (Decimal): Service level requirement reorder_cost (Decimal): Cost to place a reorder based on the cost of operations over the period interval_length (Decimal): The number of periods the demand data is grouped into e.g. 12 (months) interval_type (Decimal): months, weeks,days, years, quarters. Keyword Args: **df(pd.DataFrame): Pandas DataFrame containing the raw data in the correct format. **file_path (str) : Path to csv or txt file containing formatted data. Returns: dict/pd.DataFrame: Analysis of inventory profile. Examples: >>> from supplychainpy.model_inventory import analyse >>> from supplychainpy.sample_data.config import ABS_FILE_PATH >>> from decimal import Decimal >>> analysed_data = analyse(file_path=ABS_FILE_PATH['COMPLETE_CSV_SM'], ... z_value=Decimal(1.28), ... reorder_cost=Decimal(400), ... retail_price=Decimal(455), ... file_type='csv', ... currency='USD') >>> analysis = [demand.orders_summary() for demand in analysed_data] >>> # Example using pandas DataFrame. >>> import pandas as pd >>> raw_df = pd.read_csv(ABS_FILE_PATH['COMPLETE_CSV_SM']) >>> analyse_kv = dict( ... df=raw_df, ... start=1, ... interval_length=12, ... interval_type='months', ... z_value=Decimal(1.28), ... reorder_cost=Decimal(400), ... retail_price=Decimal(455), ... file_type='csv', ... currency='USD' ... ) >>> analysis_df = analyse(**analyse_kv) """ analysed_orders = UncertainDemand analysed_orders_collection = [] pool = multiprocessing.Pool(4) collect_kwargs = [] try: if kwargs is not None: if kwargs.get('file_path') == UNKNOWN: converted_data = list(np.array(kwargs['df'])) for i in converted_data: sku_id, orders, unit_cost, lead_time, retail_price, quantity_on_hand, backlog = \ _unpack_row(row=i, interval_length=interval_length) total_orders = 0 if quantity_on_hand is None: quantity_on_hand = 0.0 for order in orders['demand']: total_orders += int(order) analysed_orders = _analyse_orders( orders=orders, sku_id=sku_id, lead_time=lead_time, unit_cost=unit_cost, reorder_cost=reorder_cost, z_value=z_value, retail_price=retail_price, quantity_on_hand=quantity_on_hand, currency=currency, total_orders=total_orders, backlog=backlog) analysed_orders_collection.append(analysed_orders) AbcXyz(analysed_orders_collection) analysis_df = _convert_to_pandas_df(analysed_orders_collection) return analysis_df elif kwargs['file_path'] is not UNKNOWN: analysed_orders_collection = [] item_list = _clean_file(file_type=kwargs['file_type'], file_path=kwargs['file_path'], interval_length=interval_length) for sku in item_list: orders = {} sku_id, unit_cost, lead_time, retail_price, quantity_on_hand, backlog = sku.get("sku_id","UNKNOWN_SKU"), \ sku.get("unit_cost"), \ sku.get("lead_time"), \ sku.get("retail_price"), \ sku.get("quantity_on_hand"), \ sku.get("backlog") orders['demand'] = sku.get("demand") total_orders = 0 if quantity_on_hand is None: quantity_on_hand = 0.0 for order in orders['demand']: total_orders += int(order) kwargs = { 'orders': orders, 'sku_id': sku_id, 'lead_time': lead_time, 'unit_cost': unit_cost, 'reorder_cost': reorder_cost, 'z_value': z_value, 'retail_price': retail_price, 'quantity_on_hand': quantity_on_hand, 'currency': currency, 'total_orders': total_orders, 'backlog': float(backlog) } collect_kwargs.append(kwargs) analysed_orders = [ pool.apply(_analyse_orders, args=(x['orders'], x['sku_id'], x['lead_time'], x['unit_cost'], x['reorder_cost'], x['z_value'], x['retail_price'], x['quantity_on_hand'], x['currency'], x['total_orders'], x['backlog'])) for x in collect_kwargs ] #mapfunc = partial(_analyse_orders, **kwargs) #analysed_orders = pool.map(mapfunc, 0) for x in analysed_orders: analysed_orders_collection.append(x) AbcXyz(analysed_orders_collection) # analysis = [i.orders_summary() for i in analysed_orders_collection] return analysed_orders_collection elif kwargs['raw_data']: if kwargs['raw_data'] > 2: d = analyse_uncertain_demand.UncertainDemand( orders=kwargs['raw_data'], sku=kwargs['sku_id'], lead_time=kwargs['lead_time'], unit_cost=kwargs['unit_cost'], reorder_cost=reorder_cost, z_value=z_value, retail_price=kwargs['raw_data'], quantity_on_hand=kwargs['quantity_on_hand'], currency=currency) else: raise ValueError( "Data set too small. Please use a minimum of 3 entries." ) return d.orders_summary_simple() except KeyError as e: print(e)
def analyse_orders_from_file_row(file_path: str, z_value: Decimal, reorder_cost: Decimal, retail_price: Decimal, file_type: str = FileFormats.text.name, period: str = "month", length: int = 12, currency: str = 'USD') -> list: """ Analyse multiple SKUs from a file with data arranged by row. Args: file_path (file): The path to the file containing two columns of data, 1 period and 1 data-point for 1 sku. reorder_cost (Decimal): The cost to place a reorder. This is usually the cost of the operation divided by number of purchase orders placed in the previous period. z_value (Decimal): The service level required to calculate the safety stock file_type (str): Type of 'file csv' or 'text' period (str): The period of time the data points are bucketed into. length (int): The length of the period. Returns: list: A list of summaries containint Raises: Exception: Incorrect file type specified. Please specify 'csv' or 'text' for the file_type parameter. Exception: Unspecified file type, Please specify 'csv' or 'text' for file_type parameter. Examples: >>> from supplychainpy.model_inventory import analyse_orders_from_file_row >>> from supplychainpy.sample_data.config import ABS_FILE_PATH >>> analysed_data = analyse_orders_from_file_row(file_path=ABS_FILE_PATH['COMPLETE_CSV_SM'], ... reorder_cost=Decimal(45), ... z_value=Decimal(1.28), ... file_type="csv", ... retail_price=Decimal(30), ... currency='USD') """ warnings.warn( 'Analyse orders function has been deprecated. Please use the analyse function', DeprecationWarning) orders = {} analysed_orders_summary = [] analysed_orders_collection = [] item_list = {} if check_extension(file_path=file_path, file_type=file_type): if file_type == FileFormats.text.name: with open(file_path, 'r') as raw_data: item_list = (_data_cleansing.clean_orders_data_row( raw_data, length=length)) elif file_type == FileFormats.csv.name: with open(file_path, 'r') as raw_data: item_list = _data_cleansing.clean_orders_data_row_csv( raw_data, length=length) else: unspecified_file = "Unspecified file type, Please specify 'csv' or 'text' for file_type parameter." raise Exception(unspecified_file) # maybe use an iterable instead of unpacking for constructor try: for sku in item_list: sku_id = sku.get("sku_id") unit_cost = sku.get("unit_cost") lead_time = sku.get("lead_time") quantity_on_hand = sku.get("quantity_on_hand") if quantity_on_hand is None: quantity_on_hand = 0.0 orders['demand'] = sku.get("demand") analysed_orders = analyse_uncertain_demand.UncertainDemand( orders=orders, sku=sku_id, lead_time=lead_time, unit_cost=unit_cost, reorder_cost=reorder_cost, z_value=z_value, retail_price=retail_price, quantity_on_hand=quantity_on_hand, currency=currency) analysed_orders_collection.append(analysed_orders) analysed_orders_summary.append(analysed_orders.orders_summary()) orders = {} del analysed_orders except KeyError: print("The csv file is incorrectly formatted") return analysed_orders_summary
def analyse_orders_from_file_col(file_path, sku_id: str, lead_time: Decimal, unit_cost: Decimal, reorder_cost: Decimal, z_value: Decimal, retail_price: Decimal, file_type: str = FileFormats.text.name, period: str = PeriodFormats.months.name, currency='USD') -> dict: """ Analyse orders from file arranged in a single column. Analyses orders data for a single sku, using the values from a file arranged in columns.The data should be arranged in two columns, 1 for the period and the other for the corresponding data-point. Args: file_path (str): The path to the file containing two columns of data, 1 period and 1 data-point for 1 sku. sku_id (str): The unique id of the sku. lead_time (Decimal): The average lead-time for the sku over the period represented by the data, in the same unit. unit_cost (Decimal): The unit cost of the sku to the organisation. reorder_cost (Decimal): The cost to place a reorder. This is usually the cost of the operation divided by number of purchase orders placed in the previous period. z_value (Decimal): The service level required to calculate the safety stock retail_price (Decimal): The price at which the sku is retailed. file_type (str): Type of 'file csv' or 'text' period (int): The period of time the data points are bucketed into. currency (str): The currency of the source data. Returns: dict: The summary of the analysis. `{'ABC_XYZ_Classification': 'AX', 'reorder_quantity': '258', 'revenue': '2090910.44', 'average_order': '539', 'reorder_level': '813', 'economic_order_quantity': '277', 'sku': 'RR381-33', 'demand_variability': '0.052', 'economic_order_variable_cost': '29557.61', 'standard_deviation': '28', 'safety_stock': '51'}` Raises: Exception: Incorrect file type specified. Please specify 'csv' or 'text' for the file_type parameter. Exception: Unspecified file type, Please specify 'csv' or 'text' for file_type parameter. Examples: >>> from decimal import Decimal >>> from supplychainpy.model_inventory import analyse_orders_from_file_col >>> from supplychainpy.sample_data.config import ABS_FILE_PATH >>> # text file >>> RX9304_43_analysis_txt = analyse_orders_from_file_col(file_path=ABS_FILE_PATH['PARTIAL_COL_TXT_SM'], ... sku_id='RX9304-43', ... lead_time=Decimal(2), ... unit_cost=Decimal(400), ... reorder_cost=Decimal(45), ... z_value=Decimal(1.28), ... file_type='text', ... retail_price=Decimal(30)) >>> #csv file >>> RX9304_43_analysis_csv = analyse_orders_from_file_col(ABS_FILE_PATH['PARTIAL_COL_CSV_SM'], 'RX9304-43', ... reorder_cost=Decimal(45), ... unit_cost=Decimal(400), ... lead_time=Decimal(45), ... z_value=Decimal(1.28), ... file_type="csv", ... retail_price=Decimal(30)) """ orders = {} file_type_processed = '' # warnings.warn('Analyse orders function has been deprecated. Please use the analyse function', DeprecationWarning) if check_extension(file_path=file_path, file_type=file_type): if file_type == FileFormats.text.name: with open(file_path, 'r') as raw_data: orders = _data_cleansing.clean_orders_data_col_txt(raw_data) file_type_processed = 'txt' elif file_type == FileFormats.csv.name: with open(file_path) as raw_data: orders = _data_cleansing.clean_orders_data_col_csv(raw_data) file_type_processed = 'csv' else: raise Exception( 'Unspecified file type, Please specify \'csv\' or \'text\' for file_type parameter.' ) d = analyse_uncertain_demand.UncertainDemand(orders=orders, sku=sku_id, lead_time=lead_time, unit_cost=unit_cost, reorder_cost=reorder_cost, z_value=z_value, period=period, retail_price=retail_price, currency=currency) log.debug('Raw data columnar from {} analysed'.format(file_type_processed)) return d.orders_summary()