def operations_table(oper_: DataFrame) -> DataFrame: """ Главная задача функции - установка даты учета на основании дат заказа или дат поступлений Если списание не из поступлений, то 'Дата учета' = 'Дата списания остат' Если списание из поступлений и 'Дата потребности' > 'Дата списания остат', то 'Дата учета' = 'Дата потребности' Если списание из поступлений и 'Дата потребности' <= 'Дата списания остат', то 'Дата учета' = 'Дата списания остат' Так же сразу записываем файл с операциями. :param oper_: список операций :return: отформатированная таблица с операциями """ table = oper_.copy() table['Дата учета'] = table['Дата потребности'].where( table['Склад'] != 'Поступления', table['Дата списания остат']) table['Дата учета'] = table['Дата потребности'].where( (table['Склад'] == 'Поступления') & (table['Дата потребности'] > table['Дата списания остат']), table['Дата учета']) name_oper = r'.\support_data\output_tables\oper_{0}.csv'.format( NOW.strftime('%Y%m%d')) table.to_csv(name_oper, sep=";", encoding='ansi', index=False) return table
def operations_table(oper_: list) -> DataFrame: """ Главная задача функции - установка даты учета на основании дат заказа или дат поступлений Если списание не из поступлений, то 'Дата учета' = 'Дата списания остат' Если списание из поступлений и 'Дата потребности' > 'Дата списания остат', то 'Дата учета' = 'Дата потребности' Если списание из поступлений и 'Дата потребности' <= 'Дата списания остат', то 'Дата учета' = 'Дата списания остат' Так же сразу записываем файл с операциями. :param oper_: список операций :return: отформатированная таблица с операциями """ columns = [ 'Дата потребности', 'Порядковый номер', 'Заказ-Партия', 'Номенклатура потребности', 'Потребность из файла', 'Потребность нач', 'Потребность кон', 'Списание потребности', 'Склад', 'Дата списания остат', 'Номенклатура Списания', 'Остатки нач', 'Остатки кон', 'Списание остатков' ] table = DataFrame(data=oper_, columns=columns) table['Дата учета'] = table['Дата потребности'].where( table['Склад'] != 'Поступления', table['Дата списания остат']) table['Дата учета'] = table['Дата потребности'].where( (table['Склад'] == 'Поступления') & (table['Дата потребности'] > table['Дата списания остат']), table['Дата учета']) name_oper = r'.\support_data\output_tables\oper_{0}.csv'.format( NOW.strftime('%Y%m%d')) table.to_csv(name_oper, sep=";", encoding='ansi', index=False) return table
def daily_tables() -> None: """Создание таблиц для ежедневных отчетов""" name_output_req = r'.\support_data\output_tables\ask_{0}.csv'.format( NOW.strftime('%Y%m%d')) output_req = read_csv(name_output_req, sep=";", encoding='ansi', parse_dates=['Дата запуска', 'Дата начала факт'], dtype={'Номер победы': 'object'}) output_req.to_csv( f'W:\\Analytics\\Илья\\!deficit_work_files\\ask {NOW.strftime("%y%m%d %H_%M_%S")}.csv', sep=";", encoding='ansi', index=False) # запись используемых файлов, для взгляда в прошлое deficit(output_req)
def building_purchase_analysis() -> DataFrame: """Повторение алгоритма расчета потребности на файле из прошлого""" sep_date = separate_date() operations = list() dict_nom = nomenclature() dict_repl_mark = replacements(PATH_REP_MARK) dict_repl_gost = replacements(PATH_REP_GOST) dict_repl_pokr = replacements(PATH_REP_POKR) dict_repl_prochn = replacements(PATH_REP_PROCHN) start_rest_center = void_rests(dict_nom=dict_nom) start_rest_tn = void_rests(dict_nom=dict_nom) start_fut = modify_orders_to_supplier(table=load_orders_to_supplier(), dict_nom=dict_nom) start_ask = old_requirements() end_rest_center = start_rest_center.copy() end_rest_tn = start_rest_tn.copy() end_fut = start_fut.copy() end_ask = start_ask.copy() # списание остатков на потребности end_ask, end_rest_tn, end_rest_center, end_fut, operations = write_off( table=end_ask, rest_tn=end_rest_tn, rest_c=end_rest_center, fut=end_fut, oper_=operations, nom_=dict_nom, repl_={ 'mark': dict_repl_mark, 'gost': dict_repl_gost, 'pokr': dict_repl_pokr, 'prochn': dict_repl_prochn }) check_calculation_right( start_ask_=start_ask, end_ask_=end_ask, start_c_=start_rest_center, end_c_=end_rest_center, start_tn_=start_rest_tn, end_tn_=end_rest_tn, start_fut_=start_fut, end_fut_=end_fut, ) weekly_tables(start_ask_=start_ask, end_ask_=end_ask, oper_=operations, sep_date=sep_date) weekly_excel_reports() # построение таблицы----------------------------------------------- name_oper = r'.\support_data\output_tables\oper_{0}.csv'.format( NOW.strftime('%Y%m%d')) data = read_csv(name_oper, sep=";", encoding='ansi', usecols=[0, 3, 7, 10], parse_dates=['Дата потребности']) data = data[data['Дата потребности'] <= sep_date]\ [['Номенклатура потребности', 'Номенклатура Списания', 'Списание потребности']] data = data.\ groupby(by=['Номенклатура потребности', 'Номенклатура Списания']).\ sum().\ reset_index().\ rename(columns={'Номенклатура потребности': 'Номенклатура', 'Номенклатура Списания': 'Номенклатура_заказа', "Списание потребности": 'План_закупа'}) data['Заказано'] = data['План_закупа'] data = data[[ 'Номенклатура', 'План_закупа', 'Номенклатура_заказа', 'Заказано' ]] additional_plan = end_ask.copy( ) # план закупа, который остался без заказов поставщикам additional_plan = additional_plan[additional_plan['Дата запуска'] <= sep_date]\ [['Номенклатура', 'Дефицит']].\ groupby(by=['Номенклатура']).\ sum().\ reset_index().\ rename(columns={'Дефицит': 'План_закупа'}) additional_plan['Номенклатура_заказа'] = None additional_plan['Заказано'] = 0 additional_plan = additional_plan[[ 'Номенклатура', 'План_закупа', 'Номенклатура_заказа', 'Заказано' ]] additional_plan = additional_plan[additional_plan['План_закупа'] > 0] data = concat((data, additional_plan)) additional_futures = end_fut.copy() additional_futures = additional_futures[additional_futures['Количество'] > 0].\ groupby(by=['Номенклатура'])\ ['Количество'].\ sum().\ reset_index().\ rename(columns={'Номенклатура': 'Номенклатура_заказа', 'Количество': 'Заказано'}) additional_futures['Номенклатура'] = None additional_futures['План_закупа'] = 0 additional_futures = additional_futures[[ 'Номенклатура', 'План_закупа', 'Номенклатура_заказа', 'Заказано' ]] data = concat((data, additional_futures)) # добавление Поступило и остаточная потребность------------------------------------- inputs = load_orders_to_supplier() inputs = inputs[['Номенклатура', 'Заказано', 'Доставлено']].\ rename(columns={'Номенклатура': 'Номенклатура_заказа', 'Заказано': 'Заказано_всего'}).\ fillna(0) data = data.\ merge(inputs, on='Номенклатура_заказа', how='left').\ fillna(0) data['Процент_заказа'] = data['Заказано'] / data['Заказано_всего'] data['Доставлено'] = data['Доставлено'] * data['Процент_заказа'] data['Еще_заказать'] = data['План_закупа'] - data['Заказано'] data['Еще_заказать'] = data['Еще_заказать']. \ where(data['Еще_заказать'] > 0, 0) data = data. \ sort_values(by=['Номенклатура', 'Номенклатура_заказа']) data = data[[ 'Номенклатура', 'План_закупа', 'Номенклатура_заказа', 'Заказано', 'Доставлено', 'Еще_заказать' ]].fillna(0) data.\ rename(columns={'Дефицит': 'План_закупа', 'Еще_заказать': 'Остаточная_потребность'}).\ to_excel( r".\support_data\purchase_analysis\purchase_analysis.xlsx", index=False ) return data
def weekly_tables(start_ask_: DataFrame, end_ask_: DataFrame, oper_: DataFrame, sep_date: datetime = None) -> None: """Подготовка таблиц для недельных отчетов :param start_ask_: начальная поребность :param end_ask_: конечная поребность после списаний :param oper_: список операций :param sep_date: Дата разделения краткосрочного периода и долгосрочного = последний день краткосрочного """ oper_columns = [ 'Заказ-Партия', 'Номенклатура потребности', 'Склад', 'Списание потребности' ] oper_gr_columns = ['Заказ-Партия', 'Номенклатура потребности', 'Склад'] # oper_write_off - укороченная версия operations_table для мержа к output_req oper_write_off = operations_table(oper_) oper_write_off = oper_write_off[oper_columns].groupby( oper_gr_columns).sum().reset_index() center_write_off = write_off_tables(table_=oper_write_off, from_='Центральный склад') tn_write_off = write_off_tables(table_=oper_write_off, from_='ТН') future_input_write_off = write_off_tables(table_=oper_write_off, from_='Поступления') # output_req - это start_ask с изменениями для вывода в файл output_req = main_table( start_ask_=start_ask_, end_ask_=end_ask_, list_tables=[center_write_off, tn_write_off, future_input_write_off]) name_output_req = r'.\support_data\output_tables\ask_{0}.csv'.format( NOW.strftime('%Y%m%d')) output_req.to_csv(name_output_req, sep=";", encoding='ansi', index=False) # создание файлов для макроса экселя # detail_table - таблица для краткосрочного закупа if sep_date is None: # если None, то для дефицита ежедневного и далее ну нужно идти return None else: # подготовка detail.csv make_detail_table(data=output_req, sep_date=sep_date) # подготовка graf.csv и graf_without_feat.csv # эти графики только для краткосрочного периода short_term = output_req[output_req['Дата запуска'] <= sep_date] graph(table_=short_term, method='with_future_inputs') graph(table_=short_term, method='without_future_inputs') # подготовка problem_orders.csv # это заказы с проблемами для краткосрочной и долгосрочной перспективы unpr_orders = make_unapproved_orders(data=output_req, sep_date=sep_date) unpr_long_orders = make_unapproved_long_orders(data=output_req, sep_date=sep_date) problem_orders = concat([unpr_orders, unpr_long_orders], axis=0) problem_orders.to_csv( r".\support_data\data_for_reports\problem_orders.csv", sep=";", encoding='ansi', index=False) # подготовка long_nomenclature_orders.csv и long_nomenclature_possible_orders.csv # это дефицит длинной номенклатуры в долгосрочной перспективе long_nomenclature_orders(data=output_req, sep_date=sep_date)
def graph(table_: DataFrame, method: str) -> None: """Создание графика дефицита по дням и по номенклатуре для :param table_: таблица output_req из weekly_tables :param method: 'with_future_inputs' or 'without_future_inputs' """ if method == 'with_future_inputs': need_table = table_.copy() name_combin_graph = r'.\support_data\output_tables\graf_{0}.csv'.format( NOW.strftime('%Y%m%d')) name_combin_graph_excel = r".\support_data\data_for_reports\graf.csv" elif method == 'without_future_inputs': need_table = table_.copy() need_table['Остаток дефицита'] = need_table[ 'Остаток дефицита'] + need_table['Списание из Поступлений'] name_combin_graph = r'.\support_data\output_tables\graf_without_feat_{0}.csv'.format( NOW.strftime('%Y%m%d')) name_combin_graph_excel = r".\support_data\data_for_reports\graf_without_feat.csv" else: raise AttributeError( 'Argument "method" receives only 2 values: with_future_inputs or without_future_inputs' ) # clean_for_graf - output_req только с потребностями больше 0 clean_for_graf = need_table[(need_table['Остаток дефицита'] > 0) & (need_table['Заказ обеспечен'] == 0) & (need_table['Пометка удаления'] == 0)].copy() # для схлопывания указанных гостов old_nomenclature = clean_for_graf[['Номенклатура', 'Код']].copy() del old_nomenclature['Код'] old_nomenclature['Номенклатура_с_заменами_гостов'] = clean_for_graf[ 'Номенклатура'].map(lambda x: (x.replace( 'ГОСТ 7798-70', 'ГОСТ Р ИСО 4014-2013').replace( 'ГОСТ Р ИСО 4017-2013', 'ГОСТ Р ИСО 4014-2013').replace( 'ГОСТ 5915-70', 'ГОСТ ISO 4032-2014'))).drop_duplicates() clean_for_graf['Номенклатура'] = clean_for_graf['Номенклатура'].map( lambda x: (x.replace('ГОСТ 7798-70', 'ГОСТ Р ИСО 4014-2013').replace( 'ГОСТ Р ИСО 4017-2013', 'ГОСТ Р ИСО 4014-2013').replace( 'ГОСТ 5915-70', 'ГОСТ ISO 4032-2014'))) # graph_ - график-календарь по дням и по номенклатурам из поребностей graph_ = pivot_table(data=clean_for_graf, values='Остаток дефицита', columns='Дата запуска', index='Номенклатура', aggfunc='sum').fillna(0).sort_index() # создание comdin_graf файла need_date = Series(graph_.columns) need_date = need_date[need_date >= NOW] cum_column = graph_[graph_.columns[graph_.columns < NOW]].sum( axis=1) # столбец с кумулятивными данными предыдущих дней combin_graph = graph_[need_date].copy() if len( combin_graph.columns ) != 0: # проблемный случай, если в промежутке от текущего дня до тек день + 2 дня нет данных, то сформировать как нулевые combin_graph[ need_date.iloc[0]] = cum_column + combin_graph[need_date.iloc[0]] else: combin_graph['first_column'] = cum_column combin_graph['second_column'] = 0 combin_graph['third_column'] = 0 combin_graph.columns = [ NOW, NOW + timedelta(days=1), NOW + timedelta(days=2) ] # создание мультииндекса, где верхний уровень отклонение от первого дня columns = Series( combin_graph.columns).diff().map(extract_day).cumsum().replace( {None: 0}) combin_graph.columns = [columns, combin_graph.columns] filter1 = combin_graph.sum(axis=1).replace({0: None}).notna() # добавление коэффициентов, что бы потом посчитать в единицах отчета через ексель nom_data = nomenclature() old_nomenclature = old_nomenclature.merge( nom_data[['Номенклатура', 'coeff', 'Единица отчета']], left_on='Номенклатура', right_on='Номенклатура', how='left', copy=False) del old_nomenclature['Номенклатура'] old_nomenclature = old_nomenclature.drop_duplicates().\ set_index('Номенклатура_с_заменами_гостов') old_nomenclature.columns = [ range(len(old_nomenclature.columns)), old_nomenclature.columns ] combin_graph = combin_graph.merge(old_nomenclature, left_index=True, right_index=True, how='left', copy=False) combin_graph[filter1].to_csv(name_combin_graph, sep=";", encoding='ansi') combin_graph[filter1].to_csv(name_combin_graph_excel, sep=";", encoding='ansi')
def graph(table_: DataFrame, method: str) -> None: """Создание графика дефицита по дням и по номенклатуре для :param table_: таблица output_req из weekly_tables :param method: 'with_future_inputs' or 'without_future_inputs' """ if method == 'with_future_inputs': need_table = table_.copy() name_combin_graph = r'.\support_data\output_tables\graf_{0}.csv'.format( NOW.strftime('%Y%m%d')) name_combin_graph_excel = r".\support_data\data_for_reports\graf.csv" elif method == 'without_future_inputs': need_table = table_.copy() need_table['Остаток дефицита'] = need_table[ 'Остаток дефицита'] + need_table['Списание из Поступлений'] name_combin_graph = r'.\support_data\output_tables\graf_without_feat_{0}.csv'.format( NOW.strftime('%Y%m%d')) name_combin_graph_excel = r".\support_data\data_for_reports\graf_without_feat.csv" else: raise AttributeError( 'Argument "method" receives only 2 values: with_future_inputs or without_future_inputs' ) # clean_for_graf - output_req только с потребностями больше 0 clean_for_graf = need_table[(need_table['Остаток дефицита'] > 0) & (need_table['Заказ обеспечен'] == 0) & (need_table['Пометка удаления'] == 0)] # graph_ - график-календарь по дням и по номенклатурам из поребностей graph_ = pivot_table(data=clean_for_graf, values='Остаток дефицита', columns='Дата запуска', index='Номенклатура', aggfunc='sum').fillna(0).sort_index() # создание comdin_graf файла need_date = Series(graph_.columns) need_date = need_date[need_date >= NOW] cum_column = graph_[graph_.columns[graph_.columns < NOW]].sum( axis=1) # столбец с кумулятивными данными предыдущих дней combin_graph = graph_[need_date].copy() if len( combin_graph.columns ) != 0: # проблемный случай, если в промежутке от текущего дня до тек день + 2 дня нет данных, то сформировать как нулевые combin_graph[ need_date.iloc[0]] = cum_column + combin_graph[need_date.iloc[0]] else: combin_graph['first_column'] = cum_column combin_graph['second_column'] = 0 combin_graph['third_column'] = 0 combin_graph.columns = [ NOW, NOW + timedelta(days=1), NOW + timedelta(days=2) ] # создание мультииндекса, где верхний уровень отклонение от первого дня columns = Series( combin_graph.columns).diff().map(extract_day).cumsum().replace( {None: 0}) combin_graph.columns = [columns, combin_graph.columns] filter1 = combin_graph.sum(axis=1).replace({0: None}).notna() combin_graph[filter1].to_csv(name_combin_graph, sep=";", encoding='ansi') combin_graph[filter1].to_csv(name_combin_graph_excel, sep=";", encoding='ansi')
def main_deficit_table(table: DataFrame) -> DataFrame: """Создание заготовки главной таблицы ежедневного отчета по дефициту :param table: таблица с подготовленными данными output_req из deficit() """ need_columns = [ 'Номер победы', 'Партия', 'Дата запуска', 'Номенклатура', 'Количество в заказе', 'Заказчик', 'Изделие', 'Остаток дефицита', 'Дата начала факт' ] group_columns = [ 'Номер победы', 'Партия', 'Дата запуска', 'Заказчик', 'Изделие', 'Дата начала факт' ] problems = compare_with_prev_ask( table ) # готовая таблица с индикатором проблем (переносы, отклонение потреб) detail_table = table[need_columns].copy() detail_table = detail_table.groupby(by=group_columns).sum().reset_index() detail_table['Проблема'] = None detail_table['Обеспеченность'] = 1 - (detail_table['Остаток дефицита'] / detail_table['Количество в заказе']) detail_table['Остаточная потребность'] = None detail_table['Дата запуска ФАКТ'] = detail_table['Дата начала факт'] del detail_table['Дата начала факт'] detail_table = detail_table[detail_table['Остаток дефицита'] >= 0.01] detail_table = detail_table.sort_values(by=['Дата запуска']) first_table = list() for i in range(len(detail_table)): # заполнение первой таблицы отчета row = detail_table.iloc[i] first_table.append(row.to_list()) nomenclature_row = table[(table['Номер победы'] == row['Номер победы']) & (table['Партия'] == row['Партия']) & (table['Остаток дефицита'] > 0)].copy() nomenclature_row['Заказчик'] = nomenclature_row['Номенклатура'] nomenclature_row['Остаточная потребность'] = nomenclature_row[ 'Остаток дефицита'] nomenclature_row = nomenclature_row.merge( problems, how='left', on=['Номер победы', 'Партия', 'Номенклатура']) nomenclature_row['Обеспеченность'] = None nomenclature_row['Дата запуска ФАКТ'] = None row_columns = set( table.columns) - {'Заказчик', 'Остаточная потребность'} nomenclature_row[list(row_columns)] = None nomenclature_row = nomenclature_row[detail_table.columns] for ii in range(len(nomenclature_row)): first_table.append(nomenclature_row.iloc[ii].to_list()) # работа с колонками first_table = DataFrame(data=first_table, columns=detail_table.columns) first_table = first_table[[ 'Дата запуска', 'Дата запуска ФАКТ', 'Заказчик', 'Изделие', 'Номер победы', 'Партия', 'Остаточная потребность', 'Обеспеченность', 'Проблема' ]] first_table = first_table.rename( columns={ 'Дата запуска': 'Дата запуска ПЛАН', 'Заказчик': 'Заказчик/Сортамент', 'Номер победы': '№ заказа' }) first_table['Дата закрытия дефицита с учетом поступлений'] = None first_table['Примечание МТО'] = None first_table['Примечание ПО'] = None # first_table['Дата запуска ФАКТ'] = first_table['Дата запуска ФАКТ'].replace({'0': None}) # добавление колонки с датами закрытия дефицита из поступлений # если в строчке номенклатура, то дата закрытия дефицита номенклатуры из поступлений # если в строчке заказ, то дата закрытия заказа name_oper = r'.\support_data\output_tables\oper_{0}.csv'.format( NOW.strftime('%Y%m%d')) data_oper = read_csv(name_oper, sep=";", encoding='ansi', parse_dates=['Дата учета']) data_oper.to_csv( f'W:\\Analytics\\Илья\\!deficit_work_files\\oper {NOW.strftime("%y%m%d %H_%M_%S")}.csv', sep=";", encoding='ansi', index=False) # запись используемых файлов, для взгляда в прошлое done_orders = table.copy() done_orders['Заказ-Партия'] = done_orders[ 'Номер победы'] + '-' + done_orders['Партия'].map(str) done_orders = done_orders.groupby( by='Заказ-Партия')['Остаток дефицита с поступлениями'].sum( ).reset_index() done_orders = done_orders[done_orders['Остаток дефицита с поступлениями'] == 0] data_for_orders = data_oper[(data_oper['Склад'] == 'Поступления') & ( data_oper['Заказ-Партия'].isin(done_orders['Заказ-Партия']))][[ 'Заказ-Партия', 'Дата учета' ]] data_for_orders = data_for_orders.groupby( by='Заказ-Партия')['Дата учета'].max().reset_index() data_for_nomen = data_oper[(data_oper['Склад'] == 'Поступления') & (data_oper['Потребность кон'] == 0)][[ 'Заказ-Партия', 'Номенклатура потребности', 'Дата учета' ]] data_for_nomen = data_for_nomen.rename( columns={'Номенклатура потребности': 'Заказчик/Сортамент'}) first_table['Партия'] = first_table['Партия'].fillna('nan') first_table['Заказ-Партия'] = first_table['№ заказа'] + '-' + first_table[ 'Партия'].map(lambda x: str(int(x)) if (x is not 'nan') else None) first_table['Партия'] = first_table['Партия'].replace({'nan': None}) first_table['Заказ-Партия'] = first_table['Заказ-Партия'].ffill() first_table = first_table\ .merge(data_for_orders, on='Заказ-Партия', how='left')\ .rename(columns={'Дата учета': 'Дата учета заказ'}) first_table = first_table \ .merge(data_for_nomen, on=['Заказ-Партия', 'Заказчик/Сортамент'], how='left') \ .rename(columns={'Дата учета': 'Дата учета номен'}) first_table['Дата закрытия дефицита с учетом поступлений'] = first_table['Дата учета номен']\ .where(first_table['Дата запуска ПЛАН'].isna(), first_table['Дата учета заказ']) del first_table['Дата учета заказ'], first_table[ 'Дата учета номен'], first_table['Заказ-Партия'] return first_table