Beispiel #1
0
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()