import os, time, re
import pandas as pd
import shutil
from pc_fa.utils.utils import pc_utils
from pc_fa.scripts.jst_income_details_process.a00_initdata_sub import SubInitData
import warnings
from loguru import logger
warnings.filterwarnings('ignore')
"""
作者:熊仁科,Kevin.Xiong
日期:2024年9月2日
版本:V7.0
功能简介:对每个月的收入明细的csv文件进行初步的ETL
修改记录:增加了黑鲸的处理
修改记录:20240907-多件装拆分多行时除第一行以外其他行的 销售金额和当期退货金额 设置为0
修改记录:20240930-将异常处理移动到最后再来处理
修改记录:20241006-增加循环处理异常的代码处理逻辑
修改记录: 202250103 异常数据提取
1. 店铺属性=仓播项目组 且 商品属性=福袋
2. 商品编码=MD1111
3. 商品编码=MD9999
逻辑修改
1. 考核单价新增逻辑
店铺属性:仓播项目组,商品属性:福袋
取 分销商-福袋 单价
2. 商品编码=MD1111和MD9999的替换逻辑去除
修改记录: 202250716 xxxx
"""
task_type = "收入明细处理"
process_type = "收入明细处理-常规数据ETL"
# 输入:\\172.16.1.5\财务部\财务自动化\02-数据源\聚水潭\聚水潭销售主题分析-财务\02-标准数据源\yyyymm
# 输出:\\172.16.1.5\财务部\财务自动化\03-数据处理\聚水潭销售主题分析(财务)-收入明细处理\yyyymm\01-哈希拆分-正常处理
# 1,主方法
def main():
time_start = time.time()
# 1,初始化相关变量和数据
init = SubInitData()
# 2,先删除 当月 01-哈希拆分-正常处理 文件夹下的所有文件
output_folder = init.hash_split_norm_01 # 输出目录
# if os.path.exists(output_folder): shutil.rmtree(output_folder)
for sour_type in init.data_source_list:
sour_folder = os.path.join(init.sour_main_folder_income, sour_type)
if os.path.exists(sour_folder):
# 3,获取当前数据源的所有文件进行初次清洗
handle_clean_first_save_files(init, sour_folder, sour_type)
else:
logger.error('{}-[{}]-无法找到数据来源为 [{}] 的目录'.format(process_type, init.year_month, sour_type))
time_end = time.time()
logger.info('{}-[{}]-所有文件处理完毕,总耗时 {} s'.format(process_type, init.year_month, round(time_end - time_start, 2)))
# 3,获取当前数据源的所有文件进行初次清洗
def handle_clean_first_save_files(init, soure_folder, source_type):
sour_files = list(pc_utils.list_GetAllFilesByFolder(soure_folder, '.csv', '.xlsx'))
if len(sour_files) == 0:
logger.error('{}-[{}]-数据来源为 [{}] 的目录不存在数据源文件'.format(process_type, init.year_month, source_type))
else:
for seq, each_file in enumerate(sour_files):
sour_type = source_type[:1].upper()
f_name = sour_type + '-初次清洗-' + str(seq + 1) + '.csv'
time_start = time.time()
logger.info(
'{}-[{}]-正在清洗[{}]-{}/{} 个文件:{}'.format(process_type, init.year_month, source_type, seq + 1,
len(sour_files), each_file))
# 3.1,处理单个文件:处理每个csv数据源,返回dataframe对象
df = handle_clean_first_each_file(init, each_file, sour_type)
# 将原始文件ETL后输出
# df.to_excel(r'C:\Users\93255\Desktop\test\3月.xlsx', sheet_name='Sheet1', index=False) #Test Code
time_1 = time.time()
logger.info('{}-[{}]-[{}]-{}/{} 个文件清洗完毕,耗时:{} s'.format(
process_type, init.year_month, source_type, seq + 1, len(sour_files), round(time_1 - time_start, 2)))
df.insert(0, '文件名称', f_name)
# 3.2,将最后输出的结果目录输出到指定文件夹下,后面切换成哈希拆分后写入到对应文件夹下
hash_split_files(init, df, sour_type, init.hash_split_norm_01)
time_end = time.time()
logger.info(
'{}-[{}]-[{}]-{}/{} 个文件ETL完毕,耗时 {} s'.format(process_type, init.year_month, source_type, seq + 1,
len(sour_files),
round(time_end - time_start, 2)))
# 3.1,处理单个文件:处理每个csv数据源,将最后输出的结果目录输出到指定文件夹下
def handle_clean_first_each_file(init, each_file, sour_type):
# 3.1.1,对需要替换特殊列的进行替换,尤其是读取Excel文件的时候需要对订单号保留原始的数据
fields_special = ['线上子订单编号', '线上订单号', '原始线上订单号', '内部订单号', '商品编码', '款式编码', '分销商',
'订单快递单号']
dtype_dict = {col: str for col in fields_special}
if os.path.basename(each_file).endswith('csv'):
encoding = pc_utils.detect_encoding(each_file)
df_ori = pd.read_csv(filepath_or_buffer=each_file, header=0, encoding=encoding, encoding_errors="ignore",
thousands=',', low_memory=False)
elif os.path.basename(each_file).endswith('xlsx'): #
df_ori = pd.read_excel(io=each_file, sheet_name=0, dtype=dtype_dict)
df = df_ori.copy()
for field in fields_special:
df[field] = df[field].apply(pc_utils.replace_speaicl)
# 3.1.2,添加空列-填充空字符串
fields_addcol_str = ['款式属性', '商品属性', '版型', '店长']
df = pc_utils.df_fillna_special(df, fields_addcol_str, '')
# 3.1.3,添加空列-填充0
if sour_type:
fields_addcol_0 = ['折扣系数', '退货成本', '成本', '考核成本-滞销_福袋_折扣']
df = pc_utils.df_fillna_special(df, fields_addcol_0, 0)
# 3.1.4,对需要替换指定列的nan值进行替换为空字符串
fields_fillna = ['供销商', '供应商', '品牌', '分销商', '店铺', '售后分类', '产品分类', '虚拟分类',
'颜色规格', '线上颜色规格', '商品编码', '款式编码', '订单快递公司', '售后确认日期','买家留言','供应商']
for field in fields_fillna:
if field in df.columns:
df[field] = df[field].fillna('').astype(str)
else:
df[field] = ''
# 3.1.5,对数值列填充为0
fields_fill0 = ['实发数量', '当期实退数量', '当期退货数量', '销售数量', '销售金额', '当期退货数量', '当期退货金额']
df = pc_utils.df_fillna_special(df, fields_fill0, 0)
if sour_type:
df['数据来源'] = sour_type
# 3.1.6,新增列-'供销商-更'
supplier = init.get_supplier_revise()
df['供销商-更'] = df.apply(addcol_supplier, supplier=supplier, axis=1)
df['款式属性'] = df.apply(addcol_style_attr, axis=1)
init.stores_revise = init.get_stores_revise()
init.stores_kingdee = init.get_stores_kingdee()
init.stores_props = init.get_stores_props()
df[['店铺-更', '店铺属性', '金蝶名称']] = df.apply(addcol_store, axis=1, init=init, result_type='expand')
# 3.1.8,新增列-'当期退货数量-更'
if sour_type:
df['当期退货数量-更'] = df.apply(addcol_curr_return_qty, axis=1)
# 3.1.9,修改列——实发数量
# df['实发数量'] = df['供销商'].apply(lambda x: 0 if x in ['当阳垦顿商贸有限公司', '当阳品研服饰有限责任公司'] else df.get('实发数量', 0))
if sour_type:
df['实发数量'] = df.apply(
lambda x: 0 if x['供销商'] in ['当阳垦顿商贸有限公司', '当阳品研服饰有限责任公司'] else x.get('实发数量', 0), axis=1)
# 3.1.11,新增列-'数量'
if sour_type:
df['数量'] = df['实发数量'] - df['当期退货数量-更']
# 3.1.12,新增列-删除标记,0表示删除,1表示保留
if sour_type:
# del_start = time.time()
df['删除标记'] = addcol_del_flag(df, rule_expr=init.get_delflag_rules_from_excel())
df['删除标记'] = df.apply(updcol_del_flag, axis=1)
df['删除标记'] = df['删除标记'].apply(lambda x: 0 if x else 1)
# del_end = time.time()
# logger.info('删除标记耗时:{}'.format(round(del_end - del_start, 2)))
comb_info = init.get_comb_info()
df['商品编码-更'] = df['商品编码'].apply(lambda x: pc_utils.split_combine_codes(x, comb_info))
df = df.explode('商品编码-更')
# 将是多件装的行拆分后的[商品编码]更写入到[组合装商品编码]列中
df['是否重复索引'] = df.index.duplicated(keep=False)
df.loc[df['是否重复索引'], '组合装商品编码'] = df.loc[df['是否重复索引'], '商品编码-更']
df['组合装拆分索引'] = df.index
df['组合装拆分序列'] = df.groupby(['组合装拆分索引']).cumcount() + 1
df['销售金额'] = df.apply(updcol_explode_reset, axis=1, field='销售金额')
df['当期退货金额'] = df.apply(updcol_explode_reset, axis=1, field='当期退货金额')
df.drop(['是否重复索引', '组合装拆分索引', '组合装拆分序列'], axis=1, inplace=True)
df = df.reset_index(drop=True)
####
#########################以下代码从VBA中重写
df['商品编码-更'] = df.apply(update_store, axis=1)
# 添加 款式编码-更 列
style_revise = init.get_style_revise()
df['款式编码-更'] = df.apply(addcol_stylecode, axis=1, style_revise=style_revise)
# 3.1.10,修改列——产品分类
df['福袋or白坯'] = df.apply(addcol_fudai_or_baipi, axis=1)
# df['福袋or白坯'] = df['商品编码'].apply(lambda x: '福袋' if '福袋' in str(x) else '白坯')
# 修改款式编码-更
df['款式编码-更'] = df.apply(update_stylecode, axis=1)
# 添加 颜色 列
df['颜色'] = df.apply(addcol_color, axis=1)
# 添加 尺码
df['尺码'] = df.apply(addcol_size, axis=1)
# 添加 男女 列
df['男女'] = df.apply(addcol_gender, axis=1)
# 添加 版型
df['版型'] = df.apply(addcol_pattern, axis=1)
# 添加 折扣系数 和 商品属性 款式编码
init.discount_factor = init.get_discount_factor()
init.discount_factor_fudai = init.get_discount_factor_fudai()
df[['商品属性', '折扣系数']] = df.apply(addcol_dis_factor, axis=1, init=init, result_type='expand')
# 添加 单价 列,该列与 款式编码-更、尺码、颜色、供销商-更 等四列有关系
init.unit_price = init.get_uprice_basic()
init.unit_prices = init.get_uprices_basic()
init.unit_price_pc = init.get_uprice_pc_basic()
global counter1, counter2, counter3, counter4, counter5, counter_excep, counter_all
counter1, counter2, counter3, counter4, counter5, counter_excep, counter_all = 0, 0, 0, 0, 0, 0, 0
uprice_start = time.time()
df[['成本单价', '单价匹配方式']] = df.apply(addcol_uprice, axis=1, result_type='expand', init=init)
logger.warning('当前单价匹配信息:c1={},c2={},c3={},c4={},c5={},待匹配单价总数={},异常个数={}'
.format(counter1, counter2, counter3, counter4, counter5, counter_all, counter_excep))
uprice_end = time.time()
logger.info('数据总行数:{},单价匹配耗时:{} s'.format(counter_all, round(uprice_end - uprice_start, 2)))
# 添加 考核单价
init.unit_price_fd = init.get_uprice_fd()
init.unit_price_fd_ts = init.get_uprice_fd_ts()
# df.to_excel(r'C:\Users\93255\Desktop\分销部-自动化工具\20250527-分销石狮分仓\退货.xlsx', sheet_name='Sheet1',
# index=False) # Test Code
df[['考核单价', '单价匹配方式']] = df.apply(addcol_uprice_ass, axis=1, init=init, result_type='expand')
# 添加 成本,退货成本,考核成本-滞销_福袋_折扣
df[['成本', '退货成本', '考核成本-滞销_福袋_折扣']] = df.apply(addcol_cost, axis=1, result_type='expand')
if '买家账号' in df.columns:
df.drop('买家账号', axis=1, inplace=True)
# 20250228:修改 私域:需要调整收入明细表的处理逻辑,如下:
# 如果当前有“当期退货数量”就更新到 当期退货金额=当期退货数量-更*销售单价
specsub_price = init.get_specsub_price()
df['当期退货金额'] = df.apply(update_curr_return_amt, specsub_price=specsub_price, axis=1)
return df
def addcol_style_attr(row):
supplier_list = ['', '当阳垦顿商贸有限公司', '当阳品研服饰有限责任公司']
if row['供销商'] in supplier_list:
return '自有款'
elif row['供销商'] == '上海一新':
return '上海一新'
else:
return '供销款'
# 新增列-'供销商-更'
def addcol_supplier(row, **kwargs):
"""
如果[供销商]在['', '当阳垦顿商贸有限公司', '当阳品研服饰有限责任公司']中,同时[供应商]为空则取[品牌]列,否则取[供应商]列;
如果[供销商]不在['', '当阳垦顿商贸有限公司', '当阳品研服饰有限责任公司']中,则直接返回[供销商]列
:param x:
:return:
"""
supplier = kwargs['supplier']
supplier_list = ['', '当阳垦顿商贸有限公司', '当阳品研服饰有限责任公司']
if row['供销商'] in supplier_list:
if row['供应商'] != '':
res = row['供应商']
else:
res = row['品牌']
if res == '' and row['发货仓'] == '品创石狮分仓':
res = '石狮分仓'
else:
res = row['供销商']
if res in supplier.keys():
res = supplier[res]
return res
# 新增列-'店铺-更'
def addcol_store(row, **kwargs):
init = kwargs['init']
key = row['店铺'] + '$$$$$' + row['分销商']
res1 = init.stores_revise.get(key, 'ERROR')
res2 = init.stores_props.get(key, 'ERROR')
res3 = init.stores_kingdee.get(key, 'ERROR')
return res1, res2, res3
# 新增列-'当期退货数量-更'
def addcol_curr_return_qty(row):
shop_list = ['天猫-lachapellesport旗舰店', '真维斯时尚旗舰店', '米梦 -真维斯时尚旗舰店', '淘系-paulfrank大嘴猴官方旗舰店', 'WOKXSS(wos)',
'天猫-onemore官方旗舰店', 'onemore官方旗舰店', 'lachapellesport旗舰店', '宜而爽官方旗舰店', '雅鹿官方旗舰店-天猫']
distributor_list = ['当阳垦顿商贸有限公司', '上海迷笔服饰有限公司', '上海欧泊服饰有限公司', '上海青奇服饰有限公司',
'厦门米梦电子商务有限公司', '泉州金豺电子商务有限公司', '当阳品研服饰有限责任公司',
'广州酷威网络科技有限公司', '泉州铭道商贸有限公司(吴凡)', '宜而爽官方旗舰店', '天鸟供应链(杭州)有限公司', '合肥新冒贸易有限公司']
sour_type = row['数据来源']
# 下面的代码仅202503月份使用,除该月份外的代码运行需要删除
# Start
tmp_list = ['天猫-真维斯服饰品牌店', '拼多多-李时珍安枫阁专卖店', '拼多多-铭派食品保健专营店', '拼多多-李时珍铭派专卖店', '拼多多-铭派滋补品专营店', '拼多多-安枫阁食品保健专营店',
'拼多多-安枫阁医疗器械专营店', '拼多多-安枫阁器械保健专营店', '拼多多-安枫阁保健器械专营店', '拼多多-安枫阁保健医疗专营店', '拼多多-安枫阁医药健康专营店',
'拼多多-铭派医药健康专营店', '天猫-概养药业旗舰店', '天猫-安枫阁保健食品专营店', '京东-安枫阁甄选店']
# 20250425:新增 “or (sour_type == 'M' or sour_type == 'F') and df['分销商'] == '衣识热风'”
if (sour_type == 'F' and (row['店铺-更'] in tmp_list)) or (
(sour_type == 'M' or sour_type == 'F') and (row['分销商'] == '衣识热风' or row['店铺'] == '雅鹿官方旗舰店-天猫')):
return 0
# End
# **黑鲸和拉夏要放同一个数据源文件夹下面
if sour_type in ['L', 'K', 'W', 'O', 'R', 'Y', 'E', 'I', 'J', 'D']:
# or sour_type == 'K' or sour_type == 'W' or sour_type == 'O' or sour_type == 'R' or sour_type=='Y':
if row['售后分类'] == '仅退款':
res = row['当期实退数量']
else:
res = row['当期退货数量']
else:
if row['售后分类'] == '仅退款':
res = row['当期实退数量']
else:
if (row['店铺-更'] in shop_list or row['分销商'] in distributor_list):
if row['店铺-更'] == 'DEBRAND旗舰店-得物' and row['分销商'] in ['上海迷笔服饰有限公司',
'上海欧泊服饰有限公司', '上海青奇服饰有限公司']:
res = row['当期退货数量']
else:
# '店铺-更'<>'DEBRAND旗舰店-得物' 或者 分销商 不在['上海迷笔服饰有限公司', '上海欧泊服饰有限公司']
res = 0
else:
res = row['当期退货数量']
return res
# 新增列-删除标记
def addcol_del_flag(df, **kwargs):
"""
动态生成删除标记列
"""
rule_expr = kwargs['rule_expr']
print(rule_expr)
# 将字符串表达式转换为可执行的函数
def evaluate_condition(expr):
# 使用globals确保df可用(注意:需谨慎处理安全性)
return eval(expr, {"df": df}, {})
# 执行条件解析
delete_mask = evaluate_condition(rule_expr)
return delete_mask.astype(int) # 1表示删除,0表示保留
def updcol_del_flag(row):
res = row['删除标记']
if row['删除标记'] == False:
if row['订单快递公司'] == '':
if row['销售数量'] > 0 and row['销售金额'] > 0:
if row['销售金额'] / row['销售数量'] == 0.01:
res = True
elif row['当期退货数量'] > 0 and row['当期退货金额'] > 0:
if row['当期退货金额'] / row['当期退货数量'] == 0.01:
res = True
return res
def updcol_explode_reset(row, **kwargs):
field = kwargs['field']
if row['组合装拆分序列'] == 1:
return row[field]
else:
return 0
def update_store(row):
if row['商品编码-更'] == '福袋/直播一部打底/NULL':
return '福袋/通牛奶绒半高领打底衫/NULL/3XL'
else:
return row['商品编码-更']
def update_stylecode(row):
if row['款式编码-更'] == '福袋/直播一部打底':
return '通牛奶绒半高领打底衫'
else:
return row['款式编码-更']
def addcol_fudai_or_baipi(row):
# df['福袋or白坯'] = df['商品编码'].apply(lambda x: '福袋' if '福袋' in str(x) else '白坯')
if '福袋' in str(row['商品编码']) or '福袋' in str(row['商品编码-更']):
return '福袋'
else:
return '白坯'
# 新增列-款式编码-更
def addcol_stylecode(row, **kwargs):
"""
添加 款式编码-更 列
:param df:
:return:
"""
style_revise = kwargs['style_revise']
res = ''
if row['删除标记'] == 1:
s1 = row['商品编码-更']
if str(s1).upper() == 'SS532245444' or str(s1).upper() == 'SS532245445':
return 'SS532'
else:
arr = str(s1).split('/')
if len(arr) == 4:
res = arr[1]
else:
res = row['款式编码']
if res != '':
res = re.sub('1件装|2件装|JSW|唯品新|京东京造|京东自营|件数', '', str(res).upper())
rel_dict = style_revise
pattern = re.compile("|".join(map(re.escape, rel_dict.keys())))
res = pattern.sub(lambda m: rel_dict[m.group()], res)
tmp = str(res).upper().strip()
if tmp.startswith('H通') or tmp.startswith('H女'):
tmp = tmp.replace('H通', '通').replace('H女', '女')
return tmp
# 新增列-男女
def addcol_gender(row):
"""
添加 男女 列
:param df:
:return:
"""
if row['删除标记'] == 1:
tmp = str(row['款式编码-更']).lstrip()[:1]
if tmp == '通':
res = '男'
elif tmp == '女':
res = '女'
else:
res = '其他'
else:
res = ''
return res
# 新增列-颜色
def addcol_color(row):
res = ''
if row['删除标记'] == 1:
for s1 in [row['商品编码'], row['商品编码-更']]:
arr = str(s1).split('/')
if len(arr) == 4:
res = arr[2]
return res
if row['颜色规格'] == '' or str(row['颜色规格']) == '0':
res = row['线上颜色规格']
else:
res = row['颜色规格']
if res == '':
res = '空'
elif 'NULL' in str(res).upper():
res = 'NULL'
else:
# 提取 线上颜色规格 中的 中文颜色,比如 薄荷绿;S
tmp = re.findall(r'[\u4e00-\u9fa5]', res)
res = ''.join(tmp)
res = re.sub('色|加绒|长裤|九分|均码', '', str(res))
return res
# 新增列-尺码
def addcol_size(row):
"""
根据 商品编码、颜色规格、线上颜色规格和商品名称 等条件依次查找尺码
:param df:
:return:
"""
res = ''
if row['删除标记'] == 1:
s1 = str(row['商品编码-更']).upper().replace('-CP', '')
arr = str(s1).split('/')
if len(arr) == 4:
res = arr[3]
else:
s1 = str(row['商品编码']).upper().replace('-CP', '')
arr = str(s1).split('/')
if len(arr) == 4:
res = arr[3]
else:
res = pc_utils.get_size(str(row['颜色规格']))
if res == '':
res = pc_utils.get_size(str(row['线上颜色规格']))
if res == '':
res = pc_utils.get_size(str(row['商品名称']))
if res == '':
res = '错误类型:尺码未找到'
if res.upper().startswith('T'):
res = res[1:]
if res.upper().startswith('J'):
res = res[1:]
if res.upper().startswith('A'):
res = res[1:]
if res == "XXL":
res = '2XL'
return res
# 新增列-版型
def addcol_pattern(row):
"""
根据尺码获取大小版
:param df:
:return:
"""
res = ''
if row['删除标记'] == 1:
_str1 = row['尺码']
if _str1 in ["3XL", "4XL", "5XL", "6XL", "7XL", "8XL"]:
res = '大版'
elif _str1 in ["XS", "S", "M", "L", "XL", "2XL", "小版"]:
res = '小版'
else:
res = '小版'
return res
# 新增列-商品属性,折扣系数
def addcol_dis_factor(row, **kwargs):
"""
# 先判断商品编码是否为福袋,为福袋则先计算福袋的折扣系数1,如果不为福袋则做下面优先级的判断
# 优先级1:店铺更+款式编码更+发货日期,其中需要判断发货日期是否在开始日期和结束日期内
# 优先级2:店铺更+款式编码更+发货日期,其中需要判断发货日期是否大于等于开始日期
# 优先级3:款式编码更+发货日期,其中需要判断发货日期是否大于等于开始日期
# 优先级4:款式编码更+颜色
# 根据上面的条件,依次执行直到获取对应折扣系数为止
:param df:
:return:
"""
init = kwargs['init']
res1, res2 = '', -1
if row['删除标记'] == 1:
# 20250425:增加 “ or (df['分销商'] == '葛店仓线下内购会')”代码
if ('福袋' in row['商品编码'] and '线上分销' in row['店铺属性']):
res1 = '福袋'
res2 = 1
return res1, res2
else:
style_code_revise = str(row['款式编码-更']).upper().replace('HJ', '').replace('通MC', '通').replace('女MC', '女')
# if style_code_revise=='女德绒半高领打底衫':
# print(style_code_revise)
# 如果商品编码中含福袋则用 11-男女装福袋折扣系数.xlsx,否则用 10-男女装滞销品.xlsx 的数据
if '福袋' in str(row['商品编码']) or '福袋' in str(row['商品编码-更']):
res1 = '福袋'
df_factor = init.discount_factor_fudai
else:
df_factor = init.discount_factor
# 那就是要先判断有没有售后日期 如果有售后日期就优先取售后日期,否则就取发货日期
if row['售后确认日期'] != '':
delivery_time = int(time.strftime("%Y%m%d", time.strptime(row['售后确认日期'], "%Y/%m/%d %H:%M:%S")))
elif row['发货日期'] != 'NaT':
delivery_time = int(time.strftime("%Y%m%d", time.strptime(row['发货日期'], "%Y/%m/%d %H:%M:%S")))
# delivery_time = int(time.strftime("%Y%m%d", time.strptime(df['发货日期'], "%Y/%m/%d %H:%M")))
else:
delivery_time = 1
for k, v in df_factor.items():
# 1和2的优先级不同原来的地方在于是否开始日期或者结束日期
if k == 1 or k == 2:
key1 = '$$$$$'.join([str(row['店铺-更']), style_code_revise, '']).upper()
elif k == 3 or k == 5:
key1 = '$$$$$'.join(['', style_code_revise, '']).upper()
elif k == 4:
key1 = '$$$$$'.join(['', style_code_revise, str(row['颜色'])]).upper()
# 如果当前拼接的款式在每个优先级中有对应的key,则直接获取值
if key1 in v.keys(): # key1:拼接的店铺更-款式-颜色
dic_sub1 = v[key1]
# '$$$$$通速干贴条篮球短T$$$$$': {1: {'开始日期': 0, '结束日期': 20240813, '滞销': '初级滞销', '折扣系数': 0.7, '备注': ''}, 2: {'开始日期': 20240814, '结束日期': 99999999, '滞销': '初级滞销', '折扣系数': 0.6, '备注': ''}}
# dic_sub1:{1: {'开始日期': 0, '结束日期': 20240813, '滞销': '初级滞销', '折扣系数': 0.7, '备注': ''}, 2: {'开始日期': 20240814, '结束日期': 99999999, '滞销': '初级滞销', '折扣系数': 0.6, '备注': ''}
for k_sub, dic_sub in dic_sub1.items(): # k_sub:1,v_sub:{'开始日期': 0, '结束日期': 20240813, '滞销': '初级滞销', '折扣系数': 0.7, '备注': ''}
if dic_sub['开始日期'] <= delivery_time <= dic_sub['结束日期']:
res1 = dic_sub['滞销']
res2 = dic_sub['折扣系数']
return res1, res2
if res2 == -1:
# 20250429-修改:从2025年4月开始,福袋折扣系数由0.5改为0.3
# 20250624-修改:从2025年6月开始,福袋折扣系数由0.5改为0.4
if '福袋' in str(row['商品编码']) or '福袋' in str(row['商品编码-更']):
res1, res2 = "福袋", 0.4
else:
res1, res2 = "正常销售", 1
if res2 == -1: res2 = 1
return res1, res2
# 新增列-成本单价、单价匹配方式
def addcol_uprice(row, **kwargs):
global counter1, counter2, counter3, counter4, counter5, counter_excep, counter_all
init = kwargs['init']
if row['删除标记'] == 1:
# 20250430-临时方案
if str(row['款式编码-更']).upper() == '通XD208侧条拼接短裤' and row['分销商'] == '分销商-GXG品牌':
return 24.7, '特殊处理-[分销商-GXG品牌]'
# 20250601-临时方案
if str(row['款式编码-更']).upper() == '通Q感空气层弯刀裤' and row['分销商'] == '分销商-GXG品牌':
return 41.2, '特殊处理-[分销商-GXG品牌]'
counter_all += 1
# 预留-商品编码匹配-20241023
key = row['商品编码-更']
res = init.unit_price_pc.get(key, '') # 用商品编码匹配
if res != '':
return res, '商品编码匹配成功'
if pc_utils.is_numeric_except(row['尺码']) or row['尺码'] == '错误类型:尺码未找到': row['尺码'] = 'L'
key1 = '$$$$$'.join(['PC', str(row['款式编码-更']), str(row['颜色']), str(row['尺码'])]).upper()
res = init.unit_price.get(key1, '')
if res == '':
key1 = '$$$$$'.join(['PC', str(row['款式编码-更']), '不分颜色', str(row['尺码'])]).upper()
res = init.unit_price.get(key1, '')
if res == '':
style_code_revise = str(row['款式编码-更']).upper().replace('HJ', '').replace('通MC', '通').replace(
'女MC', '女')
arr_supplier = [row['供销商-更'], 'PC', '']
arr_style_code = [str(row['款式编码-更']), style_code_revise]
arr_color = ['不分颜色', row['颜色'], '其他颜色']
arr_size = ['不分尺码', row['尺码']]
for i1, supplier in enumerate(arr_supplier):
for i2, style_code in enumerate(arr_style_code):
for i3, color in enumerate(arr_color):
for i4, size in enumerate(arr_size):
key1 = '$$$$$'.join([str(supplier), str(style_code), str(color), str(size)]).upper()
# 供销商-更有可能为空
if i1 != len(arr_supplier) - 1:
res = init.unit_price.get(key1, '')
if res != '':
counter3 += 1
return res, '+'.join([str(i1), str(i2), str(i3), str(i4)])
else:
tmp = init.unit_prices.get(key1, [])
if len(tmp) == 1:
counter4 += 1
res = tmp[0]
sta = '供销商空匹配值'
return res, sta
elif len(tmp) > 1:
counter5 += 1
counter_excep += 1 # 2025-07-23新增 多单价也算错误
res = ''
sta = '错误类型:匹配多单价:' + ','.join([str(x) for x in tmp])
return res, sta
else:
counter2 += 1
return res, '2'
else:
counter1 += 1
return res, '1'
counter_excep += 1
return '', '错误类型:成本单价匹配失败'
else:
return '', ''
# 新增列-考核单价
def addcol_uprice_ass(row, **kwargs):
"""
添加福袋考核单价
"""
init = kwargs['init']
res = ''
'''
('线上分销' in df['店铺属性']) or ('抖音-微光小铺FJ' in df['店铺-更']) or ('品创仓播' in df['店铺-更'] or ('抖音-微光小铺FJ' in df['分销商']) or ('品创仓播' in df['分销商'])
'''
# 20250421:新增代码
if row['删除标记'] == 1 and (row['店铺-更'] == '莫格图云仓' or row['店铺属性'] == '仓播项目组-店铺'):
if row['成本单价'] != '':
return row['折扣系数'] * row['成本单价'], ''
else:
return '', row['单价匹配方式']
if row['删除标记'] == 1 and row['福袋or白坯'] == '福袋' and (
('线上分销' in row['店铺属性']) or
('仓播项目组' in row['店铺属性']) or
('抖音-微光小铺FJ' in row['店铺-更']) or
('品创仓播' in row['店铺-更']) or
('衣工厂' in row['店铺-更']) or
('衣 工厂' in row['店铺-更']) or
('抖音-微光小铺FJ' in row['分销商']) or
('品创仓播' in row['分销商']) or
('衣 工厂' in row['分销商']) or
('衣工厂' in row['分销商']) or
('葛店仓线下内购会' in row['分销商']) or
('当阳清仓内购会' in row['店铺属性'])):
if pc_utils.is_numeric_except(row['尺码']):
row['尺码'] = 'L'
arr_style_code = [row['款式编码-更'], str(row['款式编码-更']).replace('HJ', '')]
arr_color = ['不分颜色', row['颜色'], '其他颜色']
arr_size = ['不分尺码', row['尺码']]
for style_code in arr_style_code:
for color in arr_color:
for size in arr_size:
key1 = '$$$$$'.join(['', style_code, color, size]).upper()
# 供销商-更有可能为空
if '唐狮' in row['店铺-更']:
tmp = init.unit_price_fd_ts.get(key1, []) # 09-款式单价表-基础款.xlsx:单价表-分销商福袋唐狮
else:
tmp = init.unit_price_fd.get(key1, []) # 09-款式单价表-基础款.xlsx:单价表-分销商福袋
if len(tmp) == 1:
res = tmp[0]
return res, '线上分销福袋考核单价匹配成功'
if res == '':
sta = row['单价匹配方式'] + ',错误类型:考核单价匹配失败'
return '', sta
elif row['删除标记'] == 1 and row['成本单价'] != '':
return row['折扣系数'] * row['成本单价'], ''
return '', row['单价匹配方式']
# 新增列 '成本', '退货成本', '考核成本-滞销_福袋_折扣'
def addcol_cost(row):
"""
添加 '成本', '退货成本', '考核成本-滞销_福袋_折扣'
:param df:
:return:
"""
res_ass = ''
res_cost = ''
res_return = ''
if row['删除标记'] == 1:
if row['成本单价'] != '':
res_cost = row['成本单价'] * row['数量']
res_return = row['成本单价'] * row['当期退货数量-更']
if row['店铺属性'] == '分销商' and row['福袋or白坯'] == '福袋':
if row['考核单价'] != '':
res_ass = row['考核单价'] * row['数量']
else:
if res_cost != '':
res_ass = res_cost * row['折扣系数']
return res_cost, res_return, res_ass
def update_curr_return_amt(row, **kwargs):
specsub_price = kwargs['specsub_price']
key1 = row['店铺-更'] + "$$$$$" + row['款式编码']
if key1 in specsub_price.keys():
price = specsub_price[key1]
return price * row['当期退货数量-更']
return row['当期退货金额']
# 3.2,将最后输出的结果目录输出到指定文件夹下,后面切换成哈希拆分后写入到对应文件夹下
def hash_split_files(init, df, sour_type, out_folder):
logger.info('{}-[{}]-哈希文件拆分处理中...'.format(process_type, init.year_month))
sour_type_tmp = sour_type
time_start = time.time()
# 2,定义当前脚本需要的变量数据
# 2.1,定获取 收入明细数据-不同来源字段统一 的df对象
df_fields_mapping = pd.read_excel(io=init.dbstruct_income_details, sheet_name=0, index_col='数据库中文字段').fillna('')
if not sour_type_tmp:
sour_type_tmp = '异常字段列'
db_source_mapping = {df_fields_mapping.loc[db_field][sour_type_tmp]: db_field for db_field in
df_fields_mapping.index if df_fields_mapping.loc[db_field][sour_type_tmp]}
# 2.1,数据库中文字段 作为主键,值是每一个中文字段对应其他字段名称和值字典数据
db_fields = {}
for row_index in df_fields_mapping.index:
db_fields[row_index] = {col: df_fields_mapping.loc[row_index][col] for col in df_fields_mapping.columns.values}
os.makedirs(out_folder, exist_ok=True)
df_new = df.copy()
df_new['组合装商品编码'] = df_new['组合装商品编码'].fillna('')
# ***按照配置文件的字段添加新的列***
# key:表示数据源的字段,item:表示数据源的中文字段
# print("sour_type=" + sour_type + '=')
for key, item in db_source_mapping.items():
if key in df.keys():
df_new[item] = df[key]
else:
if sour_type != '':
df_new[item] = ''
df_new['年份'] = init.year
df_new['月份'] = init.month
df_new['年月'] = init.year_month
df_new['原始线上订单号'] = df_new['原始线上订单号'].apply(pc_utils.replace_speaicl)
df_new['店长'] = ''
df_new['样式属性'] = ''
df_new['哈希值'] = df_new['原始线上订单号'].apply(pc_utils.calculate_hash)
delivery_num = 100
df_new['哈希取模'] = df_new['哈希值'].apply(lambda x: pc_utils.calculate_hash_and_remainder(x, delivery_num))
df_new['颜色规格'] = ''
# # 根据 店铺的来源配置信息,添加一列 核算属性
store_sour = init.get_store_sour()
df_new['分销商'].fillna('')
cal_sour_list = [s.upper()[:1] for s in init.data_source_list]
df_new['核算属性'] = df_new.apply(addcol_calc_attr, store_sour=store_sour, cal_sour_list=cal_sour_list, axis=1)
# 添加 印花归属
df_new['印花归属'] = df_new.apply(addcol_stamp_ownership, axis=1)
# 新增列-'当期退货数量-更2',20240820
df_new['当期退货数量-更2'] = df_new.apply(addcol_curr_return_qty2, axis=1)
# 按照数据库的中文字段的顺序进行排列,后面可以保证所以的数据源的字段都能合并到一个csv文件中
df_new = df_new.reindex(columns=db_fields.keys())
grouped = df_new.groupby('哈希取模')
for name, group in grouped:
# 最终存入的路径
file_name = os.path.join(out_folder, str(name).zfill(3) + '.csv')
if not os.path.isfile(file_name):
group.to_csv(file_name, index=False, mode='a', encoding='utf-8-sig')
else:
group.to_csv(file_name, index=False, mode='a', encoding='utf-8-sig', header=None, errors='ignore')
time_end = time.time()
logger.info('{}-[{}]-哈希文件拆分处理完毕,耗时 {} s'.format(process_type, init.year_month, round(time_end - time_start, 2)))
# 新增列 核算属性
def addcol_calc_attr(row, **kwargs):
# 查询当前配置表有没有该月份的店铺更
store_sour = kwargs['store_sour']
cal_sour_list = kwargs['cal_sour_list']
key1 = str(row['店铺']) + "$$$$$" + str(row['分销商'])
tmp1 = store_sour.get(key1, '')
tmp2 = row['数据来源'] # 原始数据
if tmp1 != '':
if tmp1 in cal_sour_list:
if tmp1 == tmp2:
return 1
else:
return 0
elif tmp1 == '不算':
return 0
else:
return 1
else:
return 2
# 添加 印花归属
def addcol_stamp_ownership(row):
store_revise = str(row['店铺-更']).strip().upper()
combine_pro_code = str(row['组合装商品编码']).upper()
product_code_revise = str(row['商品编码-更']).strip().upper()
product_name = str(row['商品名称']).strip().upper()
if '福袋' in product_code_revise or '福袋' in product_name or '福袋' in combine_pro_code:
return '印花'
if store_revise.startswith('唯品') or \
store_revise in ['京东-GENIOLAMODE京东自营专区', '京东-回力(WARRIOR)运动服饰京东自营专区']:
return '不用区分'
elif '黑鲸' in store_revise or store_revise == '上海卓成创意设计有限公司':
return '印花'
elif row['男女'] == '其他' or (
not str(row['款式编码-更']).strip().startswith('通') and not str(row['款式编码-更']).strip().startswith(
'女')):
return '非印花'
else:
if combine_pro_code == '':
if '纯色/' in product_code_revise or '空白/' in product_code_revise:
return '非印花'
elif '通HJ' in product_code_revise or '女HJ' in product_code_revise or '福袋' in product_code_revise:
return '印花'
elif '纯色/' in product_name or '空白/' in product_name:
return '非印花'
else:
return '印花'
elif combine_pro_code.startswith('HJ') or '福袋' in combine_pro_code:
return '印花'
else:
if '纯色/' not in combine_pro_code and '空白/' not in combine_pro_code:
return '印花'
else:
if '纯色/' in product_code_revise or '空白/' in product_code_revise:
return '非印花'
else:
return '印花'
# 2.1.8.1,新增列-'当期退货数量-更2',20240820
def addcol_curr_return_qty2(row):
if row['核算属性'] == 1 and row['删除标记'] == 0 and row['当期退货金额'] != 0:
return row['当期退货数量']
else:
return row['当期退货数量-更']
if __name__ == '__main__':
main()
讲解一下代码
最新发布