def main(order_date_key):
    debug_obj.trace(low, '-' * 30)
    debug_obj.trace(low, 'Drop Order called at %s' % sim_server.NowAsString())
    start_time = time.time()

    # get the wos orders dictionary from the model object
    wos_orders_dict = model_obj.getcustomattribute('wos_orders_dict')

    # get the orders for just this date
    wos_orders_list = wos_orders_dict[order_date_key]

    # loop through and drop orders
    for order_details in wos_orders_list:
        site_name, product_name, order_qty = order_details
        try:
            new_order = sim_server.CreateOrder(product_name, order_qty,
                                               site_name)
            debug_obj.trace(
                med, ' Initial push order of %s units placed for %s, %s' %
                (order_qty, site_name, product_name))
            new_order.ordernumber = new_order.ordernumber + ' Initial WOS push'.lstrip(
            )
        except:
            debug_obj.trace(
                1, ' Initial push order failed for %s %s at %s' %
                (site_name, product_name, sim_server.NowAsString()))
            utilities_LBrands.log_error(
                ' Initial push order failed for %s %s at %s' %
                (site_name, product_name, sim_server.NowAsString()))

    debug_obj.trace(
        low, 'Drop Order complete in %s seconds' % (time.time() - start_time))
示例#2
0
def main_ss(site_obj, product_obj, order_quantity):
    debug_obj.trace(low, '-'*30)
    debug_obj.trace(low, 'IP_ss called for %s %s at %s'
                    % (site_obj.name, product_obj.name, sim_server.NowAsString()))

    # get the site product object. All the data is on this object
    site_product_obj = site_obj.getsiteproduct(product_obj.name)

    # change the order quantity to 0.0 if there was no order during this review
    if order_quantity is None:
        order_quantity = 0.0

    reorder_point = site_product_obj.reorderpoint
    reorder_up_to = site_product_obj.reorderupto
    on_hand_quantity = site_product_obj.inventory
    due_in_quantity = site_product_obj.currentorderquantity
    due_out_quantity = site_product_obj.backorderquantity
    inventory_position = on_hand_quantity + due_in_quantity - due_out_quantity - order_quantity

    if inventory_position <= reorder_point:
        replenishment_quantity = float(reorder_up_to - inventory_position)
        debug_obj.trace(high, '  Need replenishment: % units of %s for %s'
                        % (replenishment_quantity, product_obj.name, site_obj.name))
        new_order = sim_server.CreateOrder(product_obj.name, replenishment_quantity, site_obj.name)
        if new_order is not None:
            debug_obj.trace(low, ' Replenishment order of %s units placed' % replenishment_quantity)
        else:
            debug_obj.trace(1, ' Replenishment order failed for %s %s at %s'
                            % (site_obj.name, product_obj.name, sim_server.NowAsString()))
            # debug_obj.errorlog('Replenishment order failed for %s %s at %s'                       SCGX only
            #                    % (site_obj.name, product_obj.name, sim_server.NowAsString()))
    else:
        debug_obj.trace(low, ' No replenishment required at this time')
示例#3
0
def main():
    debug_obj.trace(low, '-' * 30)
    debug_obj.trace(
        low, 'OnModelEndActions called at %s' % sim_server.NowAsString())
    start_time = time.time()

    # get the daily inventory data. If output set to True, write out data
    write_bool = model_obj.getcustomattribute('write_daily_inventory')
    datafile = model_obj.getcustomattribute(
        'model_folder') + '\\' + 'Daily_Inventory.csv'
    data_field = 'daily_inventory'
    write_data(data_field, datafile, write_bool, 'wb', ',')

    write_bool = model_obj.getcustomattribute('write_validation')
    datafile = model_obj.getcustomattribute(
        'model_folder') + '\\' + 'Validation.csv'
    data_field = 'validation_data'
    write_data(data_field, datafile, write_bool, 'wb', ',')

    write_bool = model_obj.getcustomattribute('write_validation')
    datafile = model_obj.getcustomattribute(
        'model_folder') + '\\' + 'WOS_push.csv'
    data_field = 'WOS_push_data'
    write_data(data_field, datafile, write_bool, 'wb', ',')

    # write anything from the script error log
    write_bool = True
    datafile = model_obj.modelpath + '\\' + 'SIMERROR.TXT'
    data_field = 'log_error'
    write_data(data_field, datafile, write_bool, 'ab', '\t')

    debug_obj.trace(
        low, 'OnModelEndActions complete in %s minutes' %
        ((time.time() - start_time) / 60.0))
def main():
    debug_obj.trace(low, "-" * 20)
    debug_obj.trace(low,
                    "Initialize model called at " + sim_server.NowAsString())

    set_vendor_order_details()
    set_vendor_product_list()
    set_vendor_lead_times()
    set_dos_output()
    set_vendor_min_output()
示例#5
0
def main():
    debug_obj.trace(low, '-' * 30)
    debug_obj.trace(low,
                    'Inventory report called at %s' % sim_server.NowAsString())
    start_time = time.time()
    daily_inventory = model_obj.getcustomattribute('daily_inventory')
    if not daily_inventory:
        daily_inventory.append(['date_time', 'skuloc', 'item_nbr', 'on_hand'])

    custom_IP_list = model_obj.getcustomattribute('custom_IP_list')

    for site_product_obj in custom_IP_list:
        # debug_obj.trace(1, 'DELETE %s, %s' % (site_product_obj.site.name, site_product_obj.product.name))
        # IP_LBrands.main(site_product_obj.site, site_product_obj.product, 0)
        daily_inventory.append([
            sim_server.NowAsString(), site_product_obj.site.name,
            site_product_obj.product.name, site_product_obj.inventory
        ])

    model_obj.setcustomattribute('daily_inventory', daily_inventory)
    debug_obj.trace(
        low,
        'Inventory report complete in %s seconds' % (time.time() - start_time))
def main(site_obj, vendor_obj):
    debug_obj.trace(low, "-" * 20)
    debug_obj.trace(
        low, "BuildVendorOrder_03 called at " + sim_server.NowAsString())

    site_vendor_product_dict = model_obj.getcustomattribute(
        "SiteVendorProductDictionary")
    product_list = site_vendor_product_dict[site_obj.name][vendor_obj.name]
    DOS_dict = build_DOS_dict(site_obj, product_list)
    vendor_order_minimum = get_vendor_minimum(
        site_obj,
        product_list[0])  # we can use just one item in the dictionary

    # in the first pass, we just get anyone below reorder point to above reorder point
    fill_below_mins(site_obj, product_list, DOS_dict)

    # check to see if the order quantity total is above the requirement
    total_order_quantity = calculate_order_quantity(site_obj, product_list)
    # debug_obj.trace(low, " Total order quantity after first pass is %s" % total_order_quantity)
    # debug_obj.trace(low, " ")

    # if we still need more, we sort the list by lowest DOS and then add in order until we reach fill.... re-sorrting
    # after each time through the list.

    if total_order_quantity < vendor_order_minimum:
        while total_order_quantity < vendor_order_minimum:
            # sort the list of products from lowest DOS to highest
            sorted_product_list = get_DOS_list(DOS_dict)
            total_order_quantity = add_another_lot(site_obj,
                                                   sorted_product_list,
                                                   total_order_quantity,
                                                   DOS_dict)
            # debug_obj.trace(low, " Total vendor order quantity after this pass is %s" % total_order_quantity)
            # debug_obj.trace(low, " ")

    place_the_order(site_obj, product_list, DOS_dict)
    reset_sp_info(site_obj, product_list)
示例#7
0
def main(site_obj, product_obj, order_quantity):
    debug_obj.trace(low, '-'*30)
    debug_obj.trace(low, 'IP_LBrands called for %s %s at %s'
                    % (site_obj.name, product_obj.name, sim_server.NowAsString()))

    # get the site product object. All the data is on this object
    site_product_obj = site_obj.getsiteproduct(product_obj.name)

    # if there was no forecast loaded for this site - product, skip the review
    if site_product_obj.getcustomattribute('IP_check') is False:
        debug_obj.trace(med, 'This site product %s-%s has no forecast. Skipping review'
                        % (site_product_obj.site.name, site_product_obj.product.name))
        debug_obj.trace(low, 'IP_LBrands complete')
        return None

    # if the current date is less than the first forecast date, skip the review
    first_forecast = site_product_obj.getcustomattribute('first_forecast_date')
    if datetime.datetime.utcfromtimestamp(sim_server.Now()) < first_forecast:
        debug_obj.trace(low, 'IP_LBrands complete')
        debug_obj.trace(med, 'The current date is less than the first forecast %s for site product %s-%s. Skipping'
                             ' review' % (first_forecast, site_product_obj.site.name, site_product_obj.product.name))
        return None
    # record the parameters for validation output
    current_date_dt = datetime.datetime.utcfromtimestamp(sim_server.Now())
    site_name = site_product_obj.site.name
    product_name = site_product_obj.product.name
    on_hand = site_product_obj.inventory
    due_in = site_product_obj.currentorderquantity
    due_out = site_product_obj.backorderquantity
    lead_time = float(site_product_obj.getcustomattribute('lead_time'))
    lead_time_mean = float(site_product_obj.getcustomattribute('lead_time'))
    lead_time_stddev = float(site_product_obj.getcustomattribute('lead_time_stddev'))
    forecast_offset = lead_time
    end_state_probability = float(site_product_obj.getcustomattribute('end_state_probability'))
    forecast_dict = utilities_LBrands.get_snapshot_forecast(site_product_obj, current_date_dt)
    lt_demand_values = utilities_LBrands.get_forecast_values(site_product_obj,
                                                             forecast_dict, current_date_dt, lead_time)
    lt_forecast_demand_sum = sum(lt_demand_values)
    lt_forecast_demand_sum_effective = min(on_hand, lt_forecast_demand_sum)
    offset_start = current_date_dt + datetime.timedelta(days=forecast_offset)
    lt_forecast_values = utilities_LBrands.get_forecast_values(site_product_obj, forecast_dict,
                                                               offset_start, lead_time)
    lt_forecast_sum = sum(lt_forecast_values)
    lt_forecast_mean = utilities_LBrands.list_mean(lt_forecast_values)
    lt_forecast_stddev = utilities_LBrands.list_stddev(lt_forecast_values)

    rem_forecast_values = utilities_LBrands.get_forecast_values(site_product_obj,
                                                                forecast_dict, current_date_dt, 9999.0)
    rem_forecast_sum = sum(rem_forecast_values)
    rem_forecast_mean = utilities_LBrands.list_mean(rem_forecast_values)
    rem_forecast_stddev = utilities_LBrands.list_stddev(rem_forecast_values)
    # compute the reorder point using standard safety stock formula. Round answer to nearest integer
    service_level = float(site_product_obj.getcustomattribute('service_level'))
    z = utilities_LBrands.z_score_lookup(service_level)
    ss_raw = z * math.sqrt((lead_time_mean * rem_forecast_stddev**2) + (rem_forecast_mean * lead_time_stddev)**2)
    input_reorder_point = site_product_obj.reorderpoint
    reorder_point = max(round(ss_raw), input_reorder_point)
    # calculate future inventory position. Inputs: on hand, due-in, due-out, current date, forecast over lead time
    inventory_position_raw = on_hand - order_quantity + due_in - due_out - lt_forecast_demand_sum_effective
    inventory_position = round(inventory_position_raw)
    # replenish decision: if inventory position <= min (calc'ed reorder point) AND
    #    total remaining forecast > end state probability then
    #    trigger replenishment
    # if replenishment triggered, calc replenishment order: max - inventory position
    replenish_order = False
    if inventory_position <= reorder_point:
        if rem_forecast_sum > end_state_probability:
            replenish_order = True

    replenishment_quantity = None
    order_placed = None
    if replenish_order is True:
        replenishment_quantity = math.ceil(lt_forecast_sum + (reorder_point - inventory_position))
        if replenishment_quantity > 0.0:
            debug_obj.trace(med, '  Need replenishment: %s units of %s for %s'
                            % (replenishment_quantity, product_name, site_name))
            try:
                sim_server.CreateOrder(product_name, replenishment_quantity, site_name)
                debug_obj.trace(med, ' Replenishment order of %s units placed' % replenishment_quantity)
                order_placed = True
            except:
                msg = ' IP_LBrands Replenishment order failed for %s units %s %s at %s' \
                      % (replenishment_quantity, site_obj.name, product_obj.name, sim_server.NowAsString())
                debug_obj.trace(low, msg)
                utilities_LBrands.log_error(msg)
                order_placed = False
        else:
            msg = ' IP_LBrands Replenishment quantity < 0.0 for site %s, sku %s. No order placed at %s.' \
                  % (site_obj.name, product_obj.name, sim_server.NowAsString())
            debug_obj.trace(low, msg)
            utilities_LBrands.log_error(msg)
            order_placed = False
    else:
        debug_obj.trace(med, ' No replenishment required at this time')

    # record validation data
    validation_data_list = [sim_server.NowAsString(), site_name, product_name, on_hand, due_in, due_out,
                            lt_forecast_demand_sum, lt_forecast_demand_sum_effective, inventory_position_raw,
                            inventory_position, lead_time, lead_time_mean, lead_time_stddev, rem_forecast_mean,
                            rem_forecast_stddev, service_level, z, ss_raw, input_reorder_point, reorder_point,
                            lt_forecast_sum,
                            rem_forecast_sum, end_state_probability, replenish_order,
                            replenishment_quantity, order_placed]

    validation_data = model_obj.getcustomattribute('validation_data')
    if not validation_data:  # if this is the first time and validation data is empty, add in these headers
        validation_data.append(['date_time', 'skuloc', 'item_nbr', 'on_hand', ' due_in', ' due_out',
                                'lt_forecast_demand_sum', 'lt_forecast_demand_sum_effective', 'inventory position raw',
                                'inventory_position', 'lead_time',
                                'lead_time_mean', 'lead_time_stddev', 'rem_forecast_mean', 'rem_forecast_stddev',
                                'service_level', 'z', 'ss_raw', 'input_reorder_point', 'reorder_point',
                                'lt_forecast_sum',
                                'rem_forecast_sum', 'end_state_probability', ' replenish_order',
                                'replenishment_quantity', 'order_placed'])

    validation_data.append(validation_data_list)
    model_obj.setcustomattribute('validation_data', validation_data)

    debug_obj.trace(low, 'IP_LBrands complete')
示例#8
0
def main():
    debug_obj.trace(low, '-' * 30)
    debug_obj.trace(low,
                    'Initial WOS push called at %s' % sim_server.NowAsString())
    start_time = time.time()

    # create a dictionary to hold the results by datetime. We only want to schedule one event per datetime, not
    # multiple calls on the same date.
    wos_orders_dict = dict()

    # loop through the sites and products
    for site_obj in model_obj.sites:
        for site_product_obj in site_obj.products:
            debug_obj.trace(
                med, ' Reviewing site %s product %s for WOS push' %
                (site_product_obj.site.name, site_product_obj.product.name))

            # get the forecast dictionary for this site product. If it empty, skip and move to the next site product
            forecast_dict = site_product_obj.getcustomattribute(
                'forecast_dict')
            empty_dict = False
            if utilities_LBrands.is_empty(forecast_dict):
                msg = ' The forecast dictionary for %s %s is empty. Skipping WOS push' \
                      % (site_product_obj.site.name, site_product_obj.product.name)
                debug_obj.trace(med, msg)
                utilities_LBrands.log_error("".join(['Info:', msg]))
                empty_dict = True
                validation_data_list = [
                    sim_server.NowAsString(), site_obj.name,
                    site_product_obj.product.name, empty_dict, '', '', '', '',
                    '', '', '', '', '', ''
                ]
                record_validation(validation_data_list)
                continue

            # get the first snapshot date
            first_snapshot_date = site_product_obj.getcustomattribute(
                'first_snapshot_date')

            # get the first forecast date determined during initialization
            first_forecast = site_product_obj.getcustomattribute(
                'first_forecast_date')

            # get the target WOS and multiply by 7 (WOS is assumed to be in weeks).
            # sum the forecasted values from the first forecast date to first forecast date + wos days. round up.
            target_wos = float(
                site_product_obj.getcustomattribute('target_WOS')) * 7.0
            # debug_obj.trace(1, 'DELETE here 01')
            forecast_dict = utilities_LBrands.get_snapshot_forecast(
                site_product_obj, first_snapshot_date)
            # debug_obj.trace(1, 'DELETE here 02')
            forecast_quantity = utilities_LBrands.get_forecast_values(
                site_product_obj, forecast_dict, first_forecast, target_wos)
            # debug_obj.trace(1, 'DELETE here 03')
            wos_order_quantity = math.ceil(sum(forecast_quantity))
            # debug_obj.trace(1, 'DELETE here 04')

            # if the wos_order_quantity = 0.0, skip this site product
            if wos_order_quantity == 0.0:
                msg = ' The calculated WOS push quantity for %s %s was 0.0 units. Skipping WOS push' \
                      % (site_product_obj.site.name, site_product_obj.product.name)
                debug_obj.trace(med, msg)
                utilities_LBrands.log_error("".join(['Info:', msg]))
                validation_data_list = [
                    sim_server.NowAsString(), site_obj.name,
                    site_product_obj.product.name, empty_dict,
                    first_snapshot_date, first_forecast, target_wos,
                    forecast_quantity,
                    sum(forecast_quantity), wos_order_quantity, '', '', '', ''
                ]
                record_validation(validation_data_list)
                continue

            # calculate a target date to have product arrive 7 days prior to first forecast
            # calculate when to drop the order at the source as target - lead time so the product can arrive around
            # the target date
            lead_time = float(site_product_obj.getcustomattribute('lead_time'))
            target_date = first_forecast - datetime.timedelta(days=14.0)
            order_date_raw = target_date - datetime.timedelta(days=lead_time)
            order_date = order_date_raw.replace(hour=0,
                                                minute=0,
                                                second=0,
                                                microsecond=0)

            # adjust the order date if it is less than the current date so that the order will drop immediately
            current_date = datetime.datetime.utcfromtimestamp(
                sim_server.Now()) + datetime.timedelta(seconds=1)
            if order_date < current_date:
                order_date = current_date

            debug_obj.trace(
                med,
                ' WOS push order scheduled. Forcast Qty %s, order quantity: %s, '
                'first forecast date %s, target arrival date %s, lead time %s, order drop date %s'
                % (sum(forecast_quantity), wos_order_quantity, first_forecast,
                   target_date, lead_time, order_date))

            # collect the wos order details in the wos order dictionary
            if order_date in wos_orders_dict:
                # wos_orders_dict[order_date].append([site_product_obj,wos_order_quantity])
                wos_orders_dict[order_date].append([
                    site_product_obj.site.name, site_product_obj.product.name,
                    wos_order_quantity
                ])
            else:
                # wos_orders_dict[order_date] = [site_product_obj, wos_order_quantity]
                wos_orders_dict[order_date] = [[
                    site_product_obj.site.name, site_product_obj.product.name,
                    wos_order_quantity
                ]]

            # if we are writing validation data, record it here
            validation_data_list = [
                sim_server.NowAsString(), site_obj.name,
                site_product_obj.product.name, first_snapshot_date,
                first_forecast, target_wos,
                sum(forecast_quantity), wos_order_quantity, lead_time,
                target_date, order_date
            ]
            record_validation(validation_data_list)

    model_obj.setcustomattribute('wos_orders_dict', wos_orders_dict)

    for order_date_key in wos_orders_dict.keys():
        order_date = datetime.datetime.strftime(order_date_key,
                                                '%m/%d/%Y %H:%M:%S')
        sim_server.ScheduleCustomEvent('DropOrder', order_date,
                                       [order_date_key])

    debug_obj.trace(
        low, 'Initial WOS Push complete in %s minutes' %
        ((time.time() - start_time) / 60.0))
示例#9
0
def main(site_obj, product_obj, order_qty):
    debug_obj.trace(low, "-" * 20)
    debug_obj.trace(low,
                    "IP_VendorMinimum called at " + sim_server.NowAsString())

    # check to see if this product has been recently reviewed - no need to do it again.
    trigger_sp_obj = site_obj.getsiteproduct(product_obj.name)
    trigger_sp_info = get_sp_info(trigger_sp_obj)
    if trigger_sp_info == 0:
        return
    if trigger_sp_info.inventory_reviewed == datetime.datetime.utcfromtimestamp(
            sim_server.Now()):
        return

    # We find the vendor associated with this site - product
    vendor_obj = trigger_sp_obj.sources[0]

    # determine the current week index and the week index at current time + lead time of the trigger site product
    trigger_sp_lead_time = trigger_sp_info.lead_time
    current_dt = datetime.datetime.utcfromtimestamp(sim_server.Now()).date()
    week_idx = abs(int(get_week_idx(current_dt)))
    current_plus_lead_time = current_dt + datetime.timedelta(
        days=trigger_sp_lead_time)
    week_lead_time_idx = get_week_idx(current_plus_lead_time)

    # Review all products for items below reorder point. Collect the list of unique vendor names to review for
    # vendor minimum order size

    # debug_obj.trace(low," Checking inventory positions for site %s vendor %s" % (site_obj.name,vendor_obj.name))

    # Get the list of products to loop through
    site_vendor_product_dict = model_obj.getcustomattribute(
        "SiteVendorProductDictionary")
    site_vendor_product_list = site_vendor_product_dict[site_obj.name][
        vendor_obj.name]

    create_order = False
    for product_name in site_vendor_product_list:
        site_product_obj = site_obj.getsiteproduct(product_name)
        sp_info = site_product_obj.getcustomattribute("SiteProductInfo")
        lead_time = sp_info.lead_time
        lead_time_demand = calculate_future_demand(site_product_obj, lead_time,
                                                   week_idx)
        future_inventory_position = calculate_inventory_position(
            site_product_obj, lead_time_demand)
        future_week_idx = week_lead_time_idx
        if sp_info.lead_time != trigger_sp_lead_time:
            current_plus_lead_time = current_dt + datetime.timedelta(
                days=sp_info.lead_time)
            future_week_idx = abs(int(get_week_idx(current_plus_lead_time)))
        future_daily_sales = calculate_future_daily_sales(
            site_product_obj, sp_info, future_week_idx)
        reorder_point = calculate_reorder_point(site_product_obj,
                                                future_daily_sales)
        sp_info.inventory_reviewed = datetime.datetime.utcfromtimestamp(
            sim_server.Now())
        msg = " IP %s,reorder point %s for %s %s" % (
            future_inventory_position, reorder_point, site_obj.name,
            product_name)
        if future_inventory_position < reorder_point:
            create_order = True
            msg += ". IP < reorderpoint triggers an order from %s" % vendor_obj.name

        # debug_obj.trace(low,msg)

        # add this info to sp_info
        sp_info.future_inventory_position = future_inventory_position
        sp_info.future_daily_sales = future_daily_sales
        sp_info.inventory_reviewed = datetime.datetime.utcfromtimestamp(
            sim_server.Now())
        sp_info.lead_time_demand = lead_time_demand

        report_inventory_position(site_product_obj, sp_info)

    if create_order is True:
        # debug_obj.trace(low,"  Building an order for vendor %s" % vendor_obj.name)
        # BuildVendorOrder_01.main(site_obj,vendor_obj)
        BuildVendorOrder.main(site_obj, vendor_obj)
def main():
    debug_obj.trace(low, '-'*30)
    debug_obj.trace(low, 'Initialize model called at %s' % sim_server.NowAsString())
    start_time = time.time()

    # read the global variables file.
    global_variable_dict = get_gv()

    # read and store default service level and end state probability from global variables
    default_end_state_probability = global_variable_dict['DEFAULT ENDSTATE PROBABILITY']
    default_service_level = global_variable_dict['DEFAULT SERVICE LEVEL']
    default_target_wos = global_variable_dict['DEFAULT TARGET WOS']
    write_daily_inventory_bool = check_bool(global_variable_dict['OUTPUT DAILY INVENTORY'])
    write_validation_bool = check_bool(global_variable_dict['OUTPUT IP VALIDATION'])

    # set custom attributes on the model object
    model_obj.setcustomattribute('daily_inventory', [])  # a container for inventory information
    model_obj.setcustomattribute('WOS_push_data', [])
    model_obj.setcustomattribute('validation_data', [])
    model_obj.setcustomattribute('write_daily_inventory', write_daily_inventory_bool)
    model_obj.setcustomattribute('write_validation', write_validation_bool)
    model_obj.setcustomattribute('model_folder', get_model_folder())
    model_obj.setcustomattribute('log_error', [])
    model_obj.setcustomattribute('z_score_table', add_z_score_table())
    model_obj.setcustomattribute('date_dict', {})

    # clear custom output files
    file_list = [
        model_obj.getcustomattribute('model_folder') + '\\' + 'WOS_push.csv',
        model_obj.getcustomattribute('model_folder') + '\\' + 'Validation.csv',
        model_obj.getcustomattribute('model_folder') + '\\' + 'Daily_Inventory.csv'
    ]
    clear_output_files(file_list)

    # set custom attributes on each site-product
    custom_IP_list = []
    for site_obj in model_obj.sites:
        for site_product_obj in site_obj.products:
            site_product_obj.setcustomattribute('forecast_dict', {})
            site_product_obj.setcustomattribute('end_state_probability', default_end_state_probability)
            site_product_obj.setcustomattribute('service_level', default_service_level)
            site_product_obj.setcustomattribute('target_WOS', default_target_wos)
            site_product_obj.setcustomattribute('IP_check', True)

            # build a list of site_product_objects using the scripted IP
            # reset their sourcing policy to Source By Transfer so we don't accidently add more calendar events
            if site_product_obj.inventorypolicy == 3:  # Custom IP policy
                custom_IP_list.append(site_product_obj)
                site_product_obj.sourcingpolicy = 7
    model_obj.setcustomattribute('custom_IP_list', custom_IP_list)

    # create the lead time dictionary from transportation/mode times for lookup later
    lead_times_dict = get_dict_lead_times()

    # read in the forecast file and add to a dictionary on each site-product key=date, value=quantity
    global_forecast_dict = {}
    global_variable = 'FORECAST FILE'
    datafile = check_global_variable(global_variable_dict, global_variable)

    if datafile != 0:
        datafile = check_relative_path(datafile)
        if check_datafile(datafile, 'r', global_variable) is True:
            global_forecast_dict = import_forecast(global_variable, datafile)
    # read in end state probability overrides
    end_state_override = {}
    global_variable = 'OVERRIDE ENDSTATE PROB'
    datafile = check_global_variable(global_variable_dict, global_variable)
    if datafile != 0:
        datafile = check_relative_path(datafile)
        if check_datafile(datafile, 'r', global_variable) is True:
            end_state_override = import_end_state_override(global_variable, datafile)

    # read in service level overrides
    service_level_override = {}
    global_variable = 'OVERRIDE SERVICE LEVEL'
    datafile = check_global_variable(global_variable_dict, global_variable)
    if datafile != 0:
        datafile = check_relative_path(datafile)
        if check_datafile(datafile, 'r', global_variable) is True:
            service_level_override = import_service_level_override(global_variable, datafile)

    # read in target wos overrides
    target_wos_override = {}
    global_variable = 'OVERRIDE WOS TARGET'
    datafile = check_global_variable(global_variable_dict, global_variable)
    if datafile != 0:
        datafile = check_relative_path(datafile)
        if check_datafile(datafile, 'r', global_variable) is True:
            target_wos_override = import_wos_override(global_variable, datafile)
    # apply the custom data dictionaries
    for site_obj in model_obj.sites:
        for site_product_obj in site_obj.products:
            # debug_obj.trace(low, "SiteProd - %s, %s" % (site_product_obj.site.name, site_product_obj.product.name))
            apply_site_product_data('forecast_dict', site_product_obj, global_forecast_dict)
            apply_site_product_data('end_state_probability', site_product_obj, end_state_override)
            apply_site_product_data('service_level', site_product_obj, service_level_override)
            apply_site_product_data('target_WOS', site_product_obj, target_wos_override)

            lead_time_mean, lead_time_stddev = get_lead_time(site_product_obj, lead_times_dict)
            site_product_obj.setcustomattribute('lead_time', lead_time_mean)
            site_product_obj.setcustomattribute('lead_time_stddev', lead_time_stddev)

            # add the first forecast date for access later
            first_snapshot_date, first_forecast_date = get_first_forecast(site_product_obj)
            site_product_obj.setcustomattribute('first_snapshot_date', first_snapshot_date)
            site_product_obj.setcustomattribute('first_forecast_date', first_forecast_date)

    debug_obj.trace(low, 'Initialize Model complete in %s minutes' % ((time.time() - start_time)/60.0))