qrpa 1.0.57__tar.gz → 1.0.59__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qrpa might be problematic. Click here for more details.
- {qrpa-1.0.57 → qrpa-1.0.59}/PKG-INFO +1 -1
- {qrpa-1.0.57 → qrpa-1.0.59}/pyproject.toml +1 -1
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/fun_excel.py +43 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/shein_excel.py +171 -1
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/shein_lib.py +208 -10
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa.egg-info/PKG-INFO +1 -1
- {qrpa-1.0.57 → qrpa-1.0.59}/README.md +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/RateLimitedSender.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/__init__.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/db_migrator.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/feishu_bot_app.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/feishu_client.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/feishu_logic.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/fun_base.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/fun_file.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/fun_web.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/fun_win.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/mysql_module/__init__.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/mysql_module/shein_return_order_model.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/shein_daily_report_model.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/shein_mysql.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/shein_sqlite.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/shein_ziniao.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/temu_chrome.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/temu_excel.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/temu_lib.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/time_utils.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/time_utils_example.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa/wxwork.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa.egg-info/SOURCES.txt +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa.egg-info/dependency_links.txt +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/qrpa.egg-info/top_level.txt +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/setup.cfg +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/setup.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/tests/test_db_migrator.py +0 -0
- {qrpa-1.0.57 → qrpa-1.0.59}/tests/test_wxwork.py +0 -0
|
@@ -73,6 +73,49 @@ excel_color_index = {
|
|
|
73
73
|
"深蓝灰色" : 55, # #333399
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
def aggregate_by_column(data, group_by_col_name):
|
|
77
|
+
"""
|
|
78
|
+
根据指定列名对二维表数据聚合:
|
|
79
|
+
- 数字列求和
|
|
80
|
+
- 字符串列用换行符拼接
|
|
81
|
+
|
|
82
|
+
:param data: 二维列表,第一行为表头
|
|
83
|
+
:param group_by_col_name: 要聚合的列名,如 "店长"
|
|
84
|
+
:return: 聚合后的二维列表
|
|
85
|
+
"""
|
|
86
|
+
headers = data[0]
|
|
87
|
+
group_index = headers.index(group_by_col_name)
|
|
88
|
+
grouped = defaultdict(list)
|
|
89
|
+
|
|
90
|
+
# 按 group_by 列聚合行
|
|
91
|
+
for row in data[1:]:
|
|
92
|
+
key = row[group_index]
|
|
93
|
+
grouped[key].append(row)
|
|
94
|
+
|
|
95
|
+
result = [headers]
|
|
96
|
+
|
|
97
|
+
for key, rows in grouped.items():
|
|
98
|
+
agg_row = []
|
|
99
|
+
for col_idx in range(len(headers)):
|
|
100
|
+
col_values = [r[col_idx] for r in rows]
|
|
101
|
+
# 聚合字段
|
|
102
|
+
if col_idx == group_index:
|
|
103
|
+
agg_value = key
|
|
104
|
+
else:
|
|
105
|
+
# 尝试将值转为 float,如果成功就求和,否则拼接
|
|
106
|
+
try:
|
|
107
|
+
nums = [float(v) for v in col_values if
|
|
108
|
+
isinstance(v, (int, float)) or (isinstance(v, str) and v.strip() != '')]
|
|
109
|
+
agg_value = sum(nums)
|
|
110
|
+
except ValueError:
|
|
111
|
+
# 拼接字符串(去重可加 set)
|
|
112
|
+
strings = [str(v).strip() for v in col_values if str(v).strip()]
|
|
113
|
+
agg_value = '\n'.join(strings)
|
|
114
|
+
agg_row.append(agg_value)
|
|
115
|
+
result.append(agg_row)
|
|
116
|
+
|
|
117
|
+
return result
|
|
118
|
+
|
|
76
119
|
def set_cell_prefix_red(cell, n, color_name):
|
|
77
120
|
"""
|
|
78
121
|
将指定 Excel 单元格内容的前 n 个字符设置为红色。
|
|
@@ -15,6 +15,176 @@ class SheinExcel:
|
|
|
15
15
|
self.bridge = bridge
|
|
16
16
|
pass
|
|
17
17
|
|
|
18
|
+
def write_week_ntb(self):
|
|
19
|
+
excel_path = create_file_path(self.config.excel_week_report)
|
|
20
|
+
|
|
21
|
+
cache_file = f'{self.config.auto_dir}/shein/dict/new_product_to_bak_{TimeUtils.today_date()}.json'
|
|
22
|
+
dict = read_dict_from_file(cache_file)
|
|
23
|
+
# dict_store = read_dict_from_file(config.dict_store_cache)
|
|
24
|
+
|
|
25
|
+
summary_excel_data = []
|
|
26
|
+
header = []
|
|
27
|
+
dict_store_bak_stat = {}
|
|
28
|
+
for store_username, excel_data in dict.items():
|
|
29
|
+
# store_name = dict_store.get(store_username)
|
|
30
|
+
if dict_store_bak_stat.get(store_username) is None:
|
|
31
|
+
dict_store_bak_stat[store_username] = [0, 0]
|
|
32
|
+
for item in excel_data[1:]:
|
|
33
|
+
dict_store_bak_stat[store_username][0] += 1
|
|
34
|
+
if int(item[6]) == 1:
|
|
35
|
+
dict_store_bak_stat[store_username][1] += 1
|
|
36
|
+
header = excel_data[0]
|
|
37
|
+
summary_excel_data += excel_data[1:]
|
|
38
|
+
summary_excel_data = [header] + summary_excel_data
|
|
39
|
+
log(summary_excel_data)
|
|
40
|
+
sheet_name = '新品转备货款明细'
|
|
41
|
+
|
|
42
|
+
# write_data(excel_path, sheet_name, summary_excel_data)
|
|
43
|
+
# self.format_week_ntb(excel_path, sheet_name)
|
|
44
|
+
|
|
45
|
+
batch_excel_operations(excel_path, [
|
|
46
|
+
(sheet_name, 'write', summary_excel_data),
|
|
47
|
+
(sheet_name, 'format', self.format_week_ntb),
|
|
48
|
+
])
|
|
49
|
+
|
|
50
|
+
dict_key = f'{self.config.auto_dir}/shein/dict/dict_store_bak_stat_{TimeUtils.today_date()}.json'
|
|
51
|
+
write_dict_to_file(dict_key, dict_store_bak_stat)
|
|
52
|
+
|
|
53
|
+
def format_week_ntb(self, sheet):
|
|
54
|
+
beautify_title(sheet)
|
|
55
|
+
format_to_date(sheet, ['统计日期'])
|
|
56
|
+
format_to_percent(sheet, ['占比'])
|
|
57
|
+
colorize_by_field(sheet, 'SPU')
|
|
58
|
+
column_to_left(sheet, ['商品信息', '第4周SKC点击率/SKC转化率', '第4周SKC销量/SKC曝光'])
|
|
59
|
+
autofit_column(sheet, ['店铺名称', '商品信息', '第4周SKC点击率/SKC转化率', '第4周SKC销量/SKC曝光'])
|
|
60
|
+
add_borders(sheet)
|
|
61
|
+
InsertImageV2(sheet, ['SKC图片'], 'shein', 120)
|
|
62
|
+
|
|
63
|
+
def dealFundsExcelFormat(self, sheet):
|
|
64
|
+
col_a = find_column_by_data(sheet, 1, '店铺名称')
|
|
65
|
+
col_b = find_column_by_data(sheet, 1, '在途商品金额')
|
|
66
|
+
col_c = find_column_by_data(sheet, 1, '在仓商品金额')
|
|
67
|
+
col_d = find_column_by_data(sheet, 1, '待结算金额')
|
|
68
|
+
col_e = find_column_by_data(sheet, 1, '可提现金额')
|
|
69
|
+
col_f = find_column_by_data(sheet, 1, '汇总')
|
|
70
|
+
col_g = find_column_by_data(sheet, 1, '导出时间')
|
|
71
|
+
col_h = find_column_by_data(sheet, 1, '销售出库金额')
|
|
72
|
+
|
|
73
|
+
sheet.range(f'{col_a}:{col_a}').column_width = 25
|
|
74
|
+
sheet.range(f'{col_g}:{col_g}').number_format = 'yyyy-mm-dd hh:mm:ss'
|
|
75
|
+
|
|
76
|
+
last_row = sheet.range('A' + str(sheet.cells.last_cell.row)).end('up').row
|
|
77
|
+
sheet.range(f'{col_b}2').formula = f'=SUM({col_b}3:{col_b}{last_row})'
|
|
78
|
+
sheet.range(f'{col_b}2').number_format = '¥#,##0.00'
|
|
79
|
+
cell = sheet.range(f'{col_b}2')
|
|
80
|
+
cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
|
|
81
|
+
cell.api.Font.Bold = True
|
|
82
|
+
|
|
83
|
+
sheet.range(f'{col_c}2').formula = f'=SUM({col_c}3:{col_c}{last_row})'
|
|
84
|
+
sheet.range(f'{col_c}2').number_format = '¥#,##0.00'
|
|
85
|
+
cell = sheet.range(f'{col_c}2')
|
|
86
|
+
cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
|
|
87
|
+
cell.api.Font.Bold = True
|
|
88
|
+
|
|
89
|
+
sheet.range(f'{col_d}2').formula = f'=SUM({col_d}3:{col_d}{last_row})'
|
|
90
|
+
sheet.range(f'{col_d}2').number_format = '¥#,##0.00'
|
|
91
|
+
cell = sheet.range(f'{col_d}2')
|
|
92
|
+
cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
|
|
93
|
+
cell.api.Font.Bold = True
|
|
94
|
+
|
|
95
|
+
sheet.range(f'{col_e}2').formula = f'=SUM({col_e}3:{col_e}{last_row})'
|
|
96
|
+
sheet.range(f'{col_e}2').number_format = '¥#,##0.00'
|
|
97
|
+
cell = sheet.range(f'{col_e}2')
|
|
98
|
+
cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
|
|
99
|
+
cell.api.Font.Bold = True
|
|
100
|
+
|
|
101
|
+
sheet.range(f'{col_h}2').formula = f'=SUM({col_h}3:{col_h}{last_row})'
|
|
102
|
+
sheet.range(f'{col_h}2').number_format = '¥#,##0.00'
|
|
103
|
+
cell = sheet.range(f'{col_h}2')
|
|
104
|
+
cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
|
|
105
|
+
cell.api.Font.Bold = True
|
|
106
|
+
|
|
107
|
+
# 遍历可用行
|
|
108
|
+
used_range_row = sheet.range('A1').expand('down')
|
|
109
|
+
for i, cell in enumerate(used_range_row):
|
|
110
|
+
row = i + 1
|
|
111
|
+
if row < 2:
|
|
112
|
+
continue
|
|
113
|
+
# 设置数字格式
|
|
114
|
+
sheet.range(f'{col_b}{row}').number_format = '¥#,##0.00'
|
|
115
|
+
sheet.range(f'{col_c}{row}').number_format = '¥#,##0.00'
|
|
116
|
+
sheet.range(f'{col_d}{row}').number_format = '¥#,##0.00'
|
|
117
|
+
sheet.range(f'{col_e}{row}').number_format = '¥#,##0.00'
|
|
118
|
+
sheet.range(f'{col_f}{row}').formula = f'=SUM({col_b}{row}:{col_e}{row})'
|
|
119
|
+
sheet.range(f'{col_f}{row}').number_format = '¥#,##0.00'
|
|
120
|
+
sheet.range(f'{col_f}{row}').api.Font.Color = 255
|
|
121
|
+
sheet.range(f'{col_f}{row}').api.Font.Bold = True
|
|
122
|
+
|
|
123
|
+
add_borders(sheet)
|
|
124
|
+
|
|
125
|
+
def write_week_finance_report(self):
|
|
126
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/stat_fund_lz_{TimeUtils.today_date()}.json'
|
|
127
|
+
dict = read_dict_from_file(cache_file)
|
|
128
|
+
dict_key = f'{self.config.auto_dir}/shein/dict/dict_store_bak_stat_{TimeUtils.today_date()}.json'
|
|
129
|
+
dict_store_bak_stat = read_dict_from_file(dict_key)
|
|
130
|
+
data = []
|
|
131
|
+
for key, val in dict.items():
|
|
132
|
+
data.append(val)
|
|
133
|
+
log(data)
|
|
134
|
+
for item in data:
|
|
135
|
+
store_username = item[1]
|
|
136
|
+
item[9] = dict_store_bak_stat[store_username][0]
|
|
137
|
+
item[10] = dict_store_bak_stat[store_username][1]
|
|
138
|
+
|
|
139
|
+
data.sort(key=lambda row: row[8], reverse=True)
|
|
140
|
+
excel_path = create_file_path(self.config.excel_week_report)
|
|
141
|
+
sheet_name = '按店铺汇总'
|
|
142
|
+
data.insert(0, ['汇总', '', '', '', '', '', '', '', '', '', '', '', ''])
|
|
143
|
+
data.insert(0, ['店铺名称', '店铺账号', '店长', '在途商品金额', '在仓商品金额', '待结算金额', '可提现金额', '汇总',
|
|
144
|
+
'销售出库金额', '新品上架数量', '成功转备货款', '成功率', '导出时间'])
|
|
145
|
+
write_data(excel_path, sheet_name, data)
|
|
146
|
+
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
147
|
+
beautify_title(sheet)
|
|
148
|
+
|
|
149
|
+
self.dealFundsExcelFormat(sheet)
|
|
150
|
+
format_to_percent(sheet, ['成功率'], 0)
|
|
151
|
+
add_formula_for_column(sheet, '成功率', '=IF(J2=0, 0, k2/J2)', 2)
|
|
152
|
+
add_formula_for_column(sheet, '新品上架数量', "=COUNTIF('新品转备货款明细'!A:A, B3)", 3)
|
|
153
|
+
add_formula_for_column(sheet, '成功转备货款', "=COUNTIFS('新品转备货款明细'!A:A, B3, '新品转备货款明细'!G:G, 1)", 3)
|
|
154
|
+
add_sum_for_cell(sheet, ['新品上架数量', '成功转备货款'])
|
|
155
|
+
column_to_right(sheet, ['金额', '汇总'])
|
|
156
|
+
sheet.autofit()
|
|
157
|
+
delete_sheet_if_exists(wb, 'Sheet1')
|
|
158
|
+
wb.save()
|
|
159
|
+
close_excel(app, wb)
|
|
160
|
+
|
|
161
|
+
new_data = data
|
|
162
|
+
new_data = aggregate_by_column(new_data, '店长')
|
|
163
|
+
new_data_sorted = new_data[2:]
|
|
164
|
+
new_data_sorted.sort(key=lambda row: row[8], reverse=True)
|
|
165
|
+
|
|
166
|
+
sheet_name = '按店长汇总'
|
|
167
|
+
write_data(excel_path, sheet_name, data[:2] + new_data_sorted)
|
|
168
|
+
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
169
|
+
add_borders(sheet)
|
|
170
|
+
format_to_money(sheet, ['金额', '成本'])
|
|
171
|
+
format_to_datetime(sheet, ['时间'])
|
|
172
|
+
format_to_percent(sheet, ['成功率'], 0)
|
|
173
|
+
add_formula_for_column(sheet, '成功率', '=IF(J2=0, 0, k2/J2)', 2)
|
|
174
|
+
# 聚合的不能使用这种公式
|
|
175
|
+
# add_formula_for_column(sheet, '新品上架数量',"=COUNTIF('新品转备货款明细'!A:A, B3)",3)
|
|
176
|
+
# add_formula_for_column(sheet, '成功转备货款',"=COUNTIFS('新品转备货款明细'!A:A, B3, '新品转备货款明细'!G:G, 1)",3)
|
|
177
|
+
add_sum_for_cell(sheet, ['在途商品金额', '在仓商品金额', '待结算金额', '可提现金额', '汇总', '销售出库金额',
|
|
178
|
+
'新品上架数量', '成功转备货款'])
|
|
179
|
+
clear_for_cell(sheet, ['店铺账号', '导出时间'])
|
|
180
|
+
add_formula_for_column(sheet, '汇总', f'=SUM(D3:G3)', 3)
|
|
181
|
+
set_title_style(sheet)
|
|
182
|
+
column_to_right(sheet, ['金额', '汇总'])
|
|
183
|
+
autofit_column(sheet, ['店铺名称', '店铺账号', '导出时间'])
|
|
184
|
+
wb.save()
|
|
185
|
+
close_excel(app, wb)
|
|
186
|
+
WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
|
|
187
|
+
|
|
18
188
|
# 退货列表
|
|
19
189
|
def write_return_list(self, erp, start_date, end_date):
|
|
20
190
|
header = ['退货单号', '退货出库时间', '签收状态', '店铺信息', '店长', '退货类型', '退货原因', 'SKC图片', 'SKC信息', '商家SKU', '属性集', 'SKU退货数量', '平台SKU', 'ERP默认供货商', 'ERP成本', '包裹名', '包裹号', '退货计划单号', '订单号', '发货单', '退回方式', '快递名称', '运单号', '退货地址', '商家联系人', '商家手机号', '入库问题图片地址']
|
|
@@ -217,7 +387,7 @@ class SheinExcel:
|
|
|
217
387
|
format_to_money(sheet, ['单价', '金额', '成本'])
|
|
218
388
|
column_to_right(sheet, ['单价', '金额', '成本'])
|
|
219
389
|
wrap_column(sheet, ['退货原因', '退货地址', '入库问题图片地址'])
|
|
220
|
-
autofit_column(sheet, ['店铺信息','店铺别名', 'SKC信息'])
|
|
390
|
+
autofit_column(sheet, ['店铺信息', '店铺别名', 'SKC信息'])
|
|
221
391
|
column_to_left(sheet, ['店铺信息', '商家SKU', '供方货号', '属性集', 'SKC信息', '退货地址'])
|
|
222
392
|
specify_column_width(sheet, ['退货原因', 'SKC信息', '商家SKU', '退货地址'], 200 / 6)
|
|
223
393
|
InsertImageV2(sheet, ['SKC图片'])
|
|
@@ -617,6 +617,201 @@ class SheinLib:
|
|
|
617
617
|
log(f'正在获取 {self.store_name} 最近一个月出库金额: {last_item["totalCustomerAmount"]}')
|
|
618
618
|
return last_item['totalCustomerAmount']
|
|
619
619
|
|
|
620
|
+
# 获取备货信息列表
|
|
621
|
+
def get_bak_base_info(self):
|
|
622
|
+
log(f'获取备货信息列表 {self.store_name} {self.store_username}')
|
|
623
|
+
url = "https://sso.geiwohuo.com/idms/goods-skc/list"
|
|
624
|
+
pageNumber = 1
|
|
625
|
+
pageSize = 100
|
|
626
|
+
payload = {
|
|
627
|
+
"pageNumber" : pageNumber,
|
|
628
|
+
"pageSize" : pageSize,
|
|
629
|
+
"supplierCodes" : "",
|
|
630
|
+
"skcs" : "",
|
|
631
|
+
"spu" : "",
|
|
632
|
+
"c7dSaleCntBegin" : "",
|
|
633
|
+
"c7dSaleCntEnd" : "",
|
|
634
|
+
"goodsLevelIdList" : [],
|
|
635
|
+
"supplyStatus" : "",
|
|
636
|
+
"shelfStatus" : "",
|
|
637
|
+
"categoryIdList" : [],
|
|
638
|
+
"skcStockBegin" : "",
|
|
639
|
+
"skcStockEnd" : "",
|
|
640
|
+
"skuStockBegin" : "",
|
|
641
|
+
"skuStockEnd" : "",
|
|
642
|
+
"skcSaleDaysBegin" : "",
|
|
643
|
+
"skcSaleDaysEnd" : "",
|
|
644
|
+
"skuSaleDaysBegin" : "",
|
|
645
|
+
"skuSaleDaysEnd" : "",
|
|
646
|
+
"planUrgentCountBegin" : "",
|
|
647
|
+
"planUrgentCountEnd" : "",
|
|
648
|
+
"skcAvailableOrderBegin": "",
|
|
649
|
+
"skcAvailableOrderEnd" : "",
|
|
650
|
+
"skuAvailableOrderBegin": "",
|
|
651
|
+
"skuAvailableOrderEnd" : "",
|
|
652
|
+
"shelfDateBegin" : "",
|
|
653
|
+
"shelfDateEnd" : "",
|
|
654
|
+
"stockWarnStatusList" : [],
|
|
655
|
+
"labelFakeIdList" : [],
|
|
656
|
+
"sheinSaleByInventory" : "",
|
|
657
|
+
"tspIdList" : [],
|
|
658
|
+
"adviceStatus" : [],
|
|
659
|
+
"sortBy7dSaleCnt" : 2
|
|
660
|
+
}
|
|
661
|
+
response_text = fetch(self.web_page, url, payload)
|
|
662
|
+
error_code = response_text.get('code')
|
|
663
|
+
if str(error_code) != '0':
|
|
664
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
665
|
+
|
|
666
|
+
spu_list = response_text['info']['list']
|
|
667
|
+
|
|
668
|
+
skc_list = [item['skc'] for item in spu_list]
|
|
669
|
+
self.get_sku_price_v2(skc_list)
|
|
670
|
+
|
|
671
|
+
total = response_text['info']['count']
|
|
672
|
+
totalPage = math.ceil(total / pageSize)
|
|
673
|
+
for page in range(2, totalPage + 1):
|
|
674
|
+
log(f'获取备货信息商品列表 第{page}/{totalPage}页')
|
|
675
|
+
payload['pageNumber'] = page
|
|
676
|
+
response_text = fetch(self.web_page, url, payload)
|
|
677
|
+
|
|
678
|
+
new_spu_list = response_text['info']['list']
|
|
679
|
+
spu_list += new_spu_list
|
|
680
|
+
|
|
681
|
+
skc_list = [item['skc'] for item in new_spu_list]
|
|
682
|
+
self.get_sku_price_v2(skc_list)
|
|
683
|
+
|
|
684
|
+
time.sleep(0.3)
|
|
685
|
+
|
|
686
|
+
key = f'{self.store_username}'
|
|
687
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/bak_info_list_{key}.json'
|
|
688
|
+
write_dict_to_file_ex(cache_file, {key: spu_list}, [key])
|
|
689
|
+
|
|
690
|
+
return spu_list
|
|
691
|
+
|
|
692
|
+
def get_skc_week_sale_list(self, spu, skc, start_from=None):
|
|
693
|
+
dict_skc = self.get_dict_skc_week_trend_v2(spu, skc, start_from)
|
|
694
|
+
date_list = TimeUtils.get_past_7_days_list(start_from)
|
|
695
|
+
saleCnt7d = 0
|
|
696
|
+
sales_detail = []
|
|
697
|
+
for date in date_list:
|
|
698
|
+
saleCnt = get_safe_value(dict_skc.get(date, {}), 'saleCnt', 0)
|
|
699
|
+
epsUvIdx = get_safe_value(dict_skc.get(date, {}), 'epsUvIdx', 0)
|
|
700
|
+
|
|
701
|
+
saleCnt7d += saleCnt
|
|
702
|
+
sales_detail.append(f'{date}({TimeUtils.get_weekday_name(date)}): {saleCnt}/{epsUvIdx}')
|
|
703
|
+
|
|
704
|
+
sales_data = []
|
|
705
|
+
for date in date_list:
|
|
706
|
+
goodsUvIdx = get_safe_value(dict_skc.get(date, {}), 'goodsUvIdx', 0) # 商详访客
|
|
707
|
+
epsGdsCtrIdx = get_safe_value(dict_skc.get(date, {}), 'epsGdsCtrIdx', 0) # 点击率
|
|
708
|
+
|
|
709
|
+
payUvIdx = get_safe_value(dict_skc.get(date, {}), 'payUvIdx', 0) # 支付人数
|
|
710
|
+
gdsPayCtrIdx = get_safe_value(dict_skc.get(date, {}), 'gdsPayCtrIdx', 0) # 转化率
|
|
711
|
+
|
|
712
|
+
sales_data.append(f'{date}({TimeUtils.get_weekday_name(date)}): {epsGdsCtrIdx:.2%}({goodsUvIdx})/{gdsPayCtrIdx:.2%}({payUvIdx})')
|
|
713
|
+
|
|
714
|
+
return sales_detail, sales_data, saleCnt7d
|
|
715
|
+
|
|
716
|
+
def stat_new_product_to_bak(self):
|
|
717
|
+
# 直接调用 get_skc_week_actual_sales 來获取周销 计算是否能转成备货款
|
|
718
|
+
skc_list = self.get_bak_base_info() # 这个地方 不要加已上架和正常供货参数 直接取所有的skc列表
|
|
719
|
+
# 以算昨日7.2日为例 上架天数为29天(转换成上架日期),且过去7天销量达到类目备货标准和没有达到备货标准的skc数量
|
|
720
|
+
# 1.计算某个skc的上架日期
|
|
721
|
+
# 2.计算某个skc的基于某个日期的过去7天销量
|
|
722
|
+
# 3.获取叶子类目的备货标准
|
|
723
|
+
header = ['店铺账号', '店铺名称', '店长', '统计日期', 'SKC图片', '商品信息', '新品成功转备货款', '第4周SKC销量/SKC曝光', '第4周SKC点击率/SKC转化率', 'SKC', 'SPU']
|
|
724
|
+
excel_data = []
|
|
725
|
+
stat_date_list = TimeUtils.get_dates_from_first_of_month_to_yesterday()
|
|
726
|
+
for stat_date in stat_date_list:
|
|
727
|
+
# 计算 stat_date 这天 的上架日期 是 filter_shelf_date
|
|
728
|
+
filter_shelf_date = TimeUtils.get_past_nth_day(29, stat_date)
|
|
729
|
+
log(f'stat_date:{stat_date},filter_shelf_date:{filter_shelf_date}')
|
|
730
|
+
# 筛选 上架日期是 filter_shelf_date 这天的skc有哪些
|
|
731
|
+
filter_skc_list = [skc_item for skc_item in skc_list if skc_item['shelfDate'] == filter_shelf_date]
|
|
732
|
+
# 再统计 这些skc 在 stat_date 这天的 前7天销量
|
|
733
|
+
# 看看这个7天销量是否达到了类目的备货标准 统计 计数
|
|
734
|
+
for skc_item in filter_skc_list:
|
|
735
|
+
skc = skc_item['skc']
|
|
736
|
+
spu = skc_item['spu']
|
|
737
|
+
log(f'skc:{skc}, spu:{spu}')
|
|
738
|
+
|
|
739
|
+
row_item = []
|
|
740
|
+
row_item.append(self.store_username)
|
|
741
|
+
|
|
742
|
+
status_cn = skc_item.get('shelfStatus').get('name')
|
|
743
|
+
goods_level = skc_item['goodsLevel']['name']
|
|
744
|
+
goods_label = [label["name"] for label in skc_item['goodsLabelList']]
|
|
745
|
+
store_info = f'{self.store_name}\n({status_cn})\n{goods_level}\n{",".join(goods_label).strip()}\n{stat_date_list[-1]}\n{stat_date_list[0]}'
|
|
746
|
+
row_item.append(store_info)
|
|
747
|
+
store_manager = self.config.shein_store_manager.get(str(self.store_username).lower())
|
|
748
|
+
row_item.append(store_manager)
|
|
749
|
+
row_item.append(stat_date)
|
|
750
|
+
row_item.append(skc_item['picUrl'])
|
|
751
|
+
|
|
752
|
+
standard_value = (skc_item.get('stockStandard') or {}).get('value') or 0
|
|
753
|
+
|
|
754
|
+
sale_detail, sale_rate, sale_num = self.get_skc_week_sale_list(spu, skc, stat_date)
|
|
755
|
+
success = int(standard_value > 0 and sale_num >= standard_value)
|
|
756
|
+
|
|
757
|
+
categoryName = skc_item['categoryName']
|
|
758
|
+
shelfDate = skc_item['shelfDate']
|
|
759
|
+
product_info = (
|
|
760
|
+
f'SPU: {spu}\n'
|
|
761
|
+
f'SKC: {skc}\n'
|
|
762
|
+
f'上架日期: {shelfDate}\n'
|
|
763
|
+
f'类目: {categoryName}\n'
|
|
764
|
+
f'备货标准/第4周销: {standard_value}/{sale_num}\n'
|
|
765
|
+
)
|
|
766
|
+
row_item.append(product_info)
|
|
767
|
+
row_item.append(success)
|
|
768
|
+
row_item.append("\n".join(sale_detail))
|
|
769
|
+
row_item.append("\n".join(sale_rate))
|
|
770
|
+
row_item.append(skc)
|
|
771
|
+
row_item.append(spu)
|
|
772
|
+
excel_data.append(row_item)
|
|
773
|
+
|
|
774
|
+
cache_file = f'{self.config.auto_dir}/shein/dict/new_product_to_bak_{TimeUtils.today_date()}.json'
|
|
775
|
+
write_dict_to_file_ex(cache_file, {self.store_username: [header] + excel_data}, [self.store_username])
|
|
776
|
+
|
|
777
|
+
def get_funds_data_lz(self):
|
|
778
|
+
log(f'正在获取 {self.store_name} 财务数据')
|
|
779
|
+
url = "https://sso.geiwohuo.com/sso/homePage/dataOverview/v2/detail"
|
|
780
|
+
payload = {
|
|
781
|
+
"metaIndexIds": [
|
|
782
|
+
298,
|
|
783
|
+
67,
|
|
784
|
+
70,
|
|
785
|
+
72
|
|
786
|
+
],
|
|
787
|
+
}
|
|
788
|
+
response_text = fetch(self.web_page, url, payload)
|
|
789
|
+
error_code = response_text.get('code')
|
|
790
|
+
if str(error_code) != '0':
|
|
791
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
792
|
+
info = response_text.get('info')
|
|
793
|
+
num298 = 0 # 在途商品金额
|
|
794
|
+
num67 = 0 # 在仓商品金额
|
|
795
|
+
num70 = 0 # 待结算金额
|
|
796
|
+
num72 = 0 # 可提现金额
|
|
797
|
+
for item in info['list']:
|
|
798
|
+
if item['metaIndexId'] == 298:
|
|
799
|
+
num298 = item['count']
|
|
800
|
+
if item['metaIndexId'] == 67:
|
|
801
|
+
num67 = item['count']
|
|
802
|
+
if item['metaIndexId'] == 70:
|
|
803
|
+
num70 = item['count']
|
|
804
|
+
if item['metaIndexId'] == 72:
|
|
805
|
+
num72 = item['count']
|
|
806
|
+
|
|
807
|
+
outAmount = self.get_last_month_outbound_amount()
|
|
808
|
+
store_manager = self.config.shein_store_manager.get(str(self.store_username).lower())
|
|
809
|
+
NotifyItem = [f'{self.store_name}', self.store_username, store_manager, num298, num67, num70, num72, '', outAmount, '', '', '', TimeUtils.current_datetime()]
|
|
810
|
+
log(NotifyItem)
|
|
811
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/stat_fund_lz_{TimeUtils.today_date()}.json'
|
|
812
|
+
write_dict_to_file_ex(cache_file, {self.store_username: NotifyItem}, [self.store_username])
|
|
813
|
+
return NotifyItem
|
|
814
|
+
|
|
620
815
|
def get_funds_data(self):
|
|
621
816
|
log(f'正在获取 {self.store_name} 财务数据')
|
|
622
817
|
url = "https://sso.geiwohuo.com/sso/homePage/dataOverview/v2/detail"
|
|
@@ -968,7 +1163,6 @@ class SheinLib:
|
|
|
968
1163
|
return count
|
|
969
1164
|
|
|
970
1165
|
def get_week_sales_stat_detail(self):
|
|
971
|
-
global ListNotify, NotifyItem
|
|
972
1166
|
dt = self.get_dt_time()
|
|
973
1167
|
yesterday = TimeUtils.get_yesterday(dt)
|
|
974
1168
|
date_7_days_ago = TimeUtils.get_past_nth_day(6, None, '%Y-%m-%d')
|
|
@@ -1145,7 +1339,7 @@ class SheinLib:
|
|
|
1145
1339
|
item.append(stock_str)
|
|
1146
1340
|
item.append(cost_price)
|
|
1147
1341
|
item.append(supplyPrice)
|
|
1148
|
-
sale_num_list, sale_data_list = self.
|
|
1342
|
+
sale_num_list, sale_data_list = self.get_sku_week_sale_list(spu, skc, sku)
|
|
1149
1343
|
item.append("\n".join(sale_num_list))
|
|
1150
1344
|
item.append("\n".join(sale_data_list))
|
|
1151
1345
|
item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
|
|
@@ -1943,7 +2137,7 @@ class SheinLib:
|
|
|
1943
2137
|
sku_item.append(skc) # SKC
|
|
1944
2138
|
sku_item.append(spu_info['supplierCode']) # SKC货号
|
|
1945
2139
|
|
|
1946
|
-
sale_num_list, sale_data_list = self.
|
|
2140
|
+
sale_num_list, sale_data_list = self.get_sku_week_sale_list(spu, skc, sku)
|
|
1947
2141
|
sku_item.append("\n".join(sale_num_list))
|
|
1948
2142
|
sku_item.append("\n".join(sale_data_list))
|
|
1949
2143
|
sku_item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
|
|
@@ -1968,13 +2162,17 @@ class SheinLib:
|
|
|
1968
2162
|
return product_sku_list
|
|
1969
2163
|
|
|
1970
2164
|
# 获取一个skc一周内的销售趋势(商品明细中的)
|
|
1971
|
-
def get_dict_skc_week_trend_v2(self, spu, skc):
|
|
2165
|
+
def get_dict_skc_week_trend_v2(self, spu, skc, start_from=None):
|
|
1972
2166
|
dt = self.get_dt_time()
|
|
1973
2167
|
|
|
1974
|
-
date_7_days_ago = TimeUtils.
|
|
1975
|
-
log('
|
|
1976
|
-
|
|
1977
|
-
|
|
2168
|
+
date_7_days_ago, date_1_days_ago = TimeUtils.get_past_7_days_range_format(start_from, '%Y%m%d')
|
|
2169
|
+
log(date_7_days_ago, date_1_days_ago, 'dt', dt)
|
|
2170
|
+
|
|
2171
|
+
# 将字符串转换为日期对象
|
|
2172
|
+
date1 = datetime.strptime(date_1_days_ago, "%Y%m%d").date()
|
|
2173
|
+
date2 = datetime.strptime(dt, "%Y%m%d").date()
|
|
2174
|
+
if date1 > date2:
|
|
2175
|
+
send_exception(f'get_dict_skc_week_trend_v2: dt:{dt} < date_1_days_ago: {date_1_days_ago}')
|
|
1978
2176
|
|
|
1979
2177
|
cache_file = f'{self.config.auto_dir}/shein/dict/dict_skc_week_trend_{skc}_{date_7_days_ago}_{date_1_days_ago}.json'
|
|
1980
2178
|
if datetime.now().hour >= 9:
|
|
@@ -2013,7 +2211,7 @@ class SheinLib:
|
|
|
2013
2211
|
write_dict_to_file(cache_file, DictSkc)
|
|
2014
2212
|
return DictSkc
|
|
2015
2213
|
|
|
2016
|
-
def
|
|
2214
|
+
def get_sku_week_sale_list(self, spu, skc, sku):
|
|
2017
2215
|
dict_skc = self.get_dict_skc_week_trend_v2(spu, skc)
|
|
2018
2216
|
date_list = TimeUtils.get_past_7_days_list()
|
|
2019
2217
|
first_day, last_day = TimeUtils.get_past_7_days_range()
|
|
@@ -2386,7 +2584,7 @@ class SheinLib:
|
|
|
2386
2584
|
if mode in [6] and sales7cn > 0:
|
|
2387
2585
|
continue
|
|
2388
2586
|
|
|
2389
|
-
sale_num_list, sale_data_list = self.
|
|
2587
|
+
sale_num_list, sale_data_list = self.get_sku_week_sale_list(spu, skc, sku)
|
|
2390
2588
|
row_item.append("\n".join(sale_num_list))
|
|
2391
2589
|
row_item.append("\n".join(sale_data_list))
|
|
2392
2590
|
row_item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|