qrpa 1.0.22__py3-none-any.whl → 1.0.24__py3-none-any.whl

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/shein_excel.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from .fun_excel import *
2
- from .fun_base import log
2
+ from .fun_base import log, calculate_star_symbols
3
3
  from .fun_file import read_dict_from_file, read_dict_from_file_ex, write_dict_to_file, write_dict_to_file_ex, delete_file
4
4
  from .time_utils import TimeUtils
5
5
  from .wxwork import WxWorkBot
@@ -11,6 +11,192 @@ class SheinExcel:
11
11
  self.config = config
12
12
  pass
13
13
 
14
+ def format_funds(self, sheet):
15
+ beautify_title(sheet)
16
+ column_to_right(sheet, ['金额', '汇总'])
17
+ format_to_money(sheet, ['金额', '汇总'])
18
+ add_sum_for_cell(sheet, ['在途商品金额', '在仓商品金额', '待结算金额', '可提现金额', '销售出库金额', '汇总'])
19
+ add_formula_for_column(sheet, '汇总', '=SUM(D3:G3)', 3)
20
+ sheet.autofit()
21
+
22
+ def format_bad_comment(self, sheet):
23
+ beautify_title(sheet)
24
+ column_to_left(sheet, ['商品信息'])
25
+ autofit_column(sheet, ['买家评价', '时间信息', '标签关键词'])
26
+ specify_column_width(sheet, ['买家评价', '商品信息'], 150 / 6)
27
+ color_for_column(sheet, ['买家评分'], '红色')
28
+ colorize_by_field(sheet, 'skc')
29
+ add_borders(sheet)
30
+ InsertImageV2(sheet, ['商品图片', '图1', '图2', '图3', '图4', '图5'])
31
+
32
+ def write_bad_comment(self):
33
+ excel_path = create_file_path(self.config.excel_bad_comment)
34
+ header = ['评价ID', '商品图片', '商品信息', '买家评分', '买家评价', '标签关键词', '区域', '时间信息', '有图', '图1',
35
+ '图2', '图3', '图4', '图5', 'skc']
36
+ summary_excel_data = [header]
37
+
38
+ cache_file = f'{self.config.auto_dir}/shein/dict/comment_list_{TimeUtils.today_date()}.json'
39
+ dict = read_dict_from_file(cache_file)
40
+ dict_store = read_dict_from_file(self.config.shein_store_alias)
41
+
42
+ for store_username, comment_list in dict.items():
43
+ store_name = dict_store.get(store_username)
44
+ sheet_name = store_name
45
+
46
+ store_excel_data = [header]
47
+ for comment in comment_list:
48
+ row_item = []
49
+ row_item.append(comment['commentId'])
50
+ row_item.append(comment['goodsThumb'])
51
+ product_info = f'属性:{comment["goodsAttribute"]}\n货号:{comment["goodSn"]}\nSPU:{comment["spu"]}\nSKC:{comment["skc"]}\nSKU:{comment["sku"]}'
52
+ row_item.append(product_info)
53
+ row_item.append(calculate_star_symbols(comment['goodsCommentStar']))
54
+ row_item.append(comment['goodsCommentContent'])
55
+ qualityLabel = '存在质量问题\n' if comment['isQualityLabel'] == 1 else ''
56
+ bad_comment_label = qualityLabel + '\n'.join([item['labelName'] for item in comment['badCommentLabelList']])
57
+
58
+ row_item.append(bad_comment_label)
59
+ row_item.append(comment['dataCenterName'])
60
+ time_info = f'下单时间:{comment["orderTime"]}\n评论时间:{comment["commentTime"]}'
61
+ row_item.append(time_info)
62
+
63
+ # 获取图片数量
64
+ image_num = len(comment.get('goodsCommentImages', []))
65
+ # 设置imgFlag值(如果comment中没有imgFlag字段,默认设为0)
66
+ imgFlag = image_num if comment.get('imgFlag') == 1 else 0
67
+ row_item.append(imgFlag)
68
+
69
+ images = comment.get('goodsCommentImages', [])
70
+ for i in range(5):
71
+ row_item.append(images[i] if i < len(images) else '')
72
+
73
+ row_item.append(comment['skc'])
74
+
75
+ store_excel_data.append(row_item)
76
+ summary_excel_data.append(row_item)
77
+
78
+ # write_data(excel_path, sheet_name, store_excel_data)
79
+ # format_bad_comment(excel_path, sheet_name)
80
+
81
+ sheet_name = 'Sheet1'
82
+
83
+ batch_excel_operations(excel_path, [
84
+ (sheet_name, 'write', summary_excel_data),
85
+ (sheet_name, 'format', self.format_bad_comment),
86
+ ])
87
+
88
+ def write_funds(self):
89
+ cache_file = f'{self.config.auto_dir}/shein/cache/stat_fund_{TimeUtils.today_date()}.json'
90
+ dict = read_dict_from_file(cache_file)
91
+ data = []
92
+ for key, val in dict.items():
93
+ data.append(val)
94
+
95
+ excel_path = create_file_path(self.config.excel_shein_fund)
96
+ sheet_name = 'Sheet1'
97
+ data.insert(0, ['汇总', '', '', '', '', '', '', '', '', ''])
98
+ data.insert(0, ['店铺名称', '店铺账号', '店长', '在途商品金额', '在仓商品金额', '待结算金额', '可提现金额',
99
+ '销售出库金额', '汇总', '导出时间'])
100
+ batch_excel_operations(excel_path, [
101
+ ('Sheet1', 'write', sort_by_column(data, 7, 2)),
102
+ ('Sheet1', 'format', self.format_funds),
103
+ ])
104
+ WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
105
+
106
+ def format_skc_quality(self, sheet):
107
+ beautify_title(sheet)
108
+ colorize_by_field(sheet, 'skc')
109
+ add_borders(sheet)
110
+ InsertImageV2(sheet, ['商品图片'])
111
+
112
+ def sort_site_desc_by_sale_cnt_14d(self, data, reverse=True):
113
+ """
114
+ 对data中的site_desc_vo_list按照skc_site_sale_cnt_14d进行排序
115
+
116
+ 参数:
117
+ data: 包含site_desc_vo_list的字典
118
+ reverse: 是否倒序排序,默认为True(从大到小)
119
+
120
+ 返回:
121
+ 排序后的data(原数据会被修改)
122
+ """
123
+ if 'site_desc_vo_list' in data and isinstance(data['site_desc_vo_list'], list):
124
+ # 处理None值,将它们放在排序结果的最后
125
+ data['site_desc_vo_list'].sort(
126
+ key=lambda x: float('-inf') if x.get('skc_site_sale_cnt_14d') is None else x['skc_site_sale_cnt_14d'],
127
+ reverse=reverse
128
+ )
129
+ return data
130
+
131
+ def write_skc_quality_estimate(self):
132
+ excel_path = create_file_path(self.config.excel_skc_quality_estimate)
133
+ header = ['店铺信息', '商品图片', '统计日期', '国家', '当日销量', '14日销量', '14日销量占比', '质量等级',
134
+ '客评数/客评分', '差评数/差评率', '退货数/退货率', 'skc', 'skc当日销量', 'skc14日销量', 'skc14日销量占比']
135
+ summary_excel_data = [header]
136
+
137
+ stat_date = TimeUtils.before_yesterday()
138
+ cache_file = f'{self.config.auto_dir}/shein/dict/googs_estimate_{stat_date}.json'
139
+ dict = read_dict_from_file(cache_file)
140
+ if len(dict) == 0:
141
+ log('昨日质量评估数据不存在')
142
+ return
143
+
144
+ dict_store = read_dict_from_file(self.config.shein_store_alias)
145
+
146
+ operations = []
147
+ for store_username, skc_list in dict.items():
148
+ store_name = dict_store.get(store_username)
149
+ sheet_name = store_name
150
+
151
+ store_excel_data = [header]
152
+ for skc_item in skc_list:
153
+ sorted_skc_item = self.sort_site_desc_by_sale_cnt_14d(skc_item, True)
154
+ # for site in sorted_skc_item['site_desc_vo_list']:
155
+ # print(f"{site['country_site']}: {site['skc_site_sale_cnt_14d']}")
156
+ # continue
157
+ store_info = f'{store_name}'
158
+ skc = sorted_skc_item['skc']
159
+ sites = sorted_skc_item['site_desc_vo_list']
160
+ skc_sale_cnt = sorted_skc_item['skc_sale_cnt']
161
+ skc_sale_cnt_14d = sorted_skc_item['skc_sale_cnt_14d']
162
+ skc_sale_rate_14d = sorted_skc_item['skc_sale_rate_14d']
163
+ for site in sites:
164
+ row_item = []
165
+ row_item.append(store_info)
166
+ row_item.append(skc_item['goods_image'])
167
+ row_item.append(stat_date)
168
+ row_item.append(site['country_site'])
169
+ row_item.append(site['skc_site_sale_cnt'])
170
+ cnt_14d = site['skc_site_sale_cnt_14d']
171
+ if cnt_14d is None or cnt_14d <= 0:
172
+ continue
173
+ row_item.append(cnt_14d)
174
+ row_item.append(site['skc_site_sale_rate_14d'])
175
+ row_item.append(site['quality_level'])
176
+ customer_info = f'{site["customer_evaluate_num"]}/{site["customer_evaluate_score"][:-1]}'
177
+ row_item.append(customer_info)
178
+ negative_info = f'{site["negative_quantity"]}/{site["negative_percent"]}'
179
+ row_item.append(negative_info)
180
+ return_info = f'{site["goods_return_quantity"]}/{site["goods_return_percent"]}'
181
+ row_item.append(return_info)
182
+ row_item.append(skc)
183
+ row_item.append(skc_sale_cnt)
184
+ row_item.append(skc_sale_cnt_14d)
185
+ row_item.append(skc_sale_rate_14d)
186
+ store_excel_data.append(row_item)
187
+ summary_excel_data.append(row_item)
188
+
189
+ operations.append((
190
+ sheet_name, 'write', store_excel_data
191
+ ))
192
+ operations.append((
193
+ sheet_name, 'format', self.format_skc_quality
194
+ ))
195
+ operations.append((
196
+ 'Sheet1', 'delete'
197
+ ))
198
+ batch_excel_operations(excel_path, operations)
199
+
14
200
  def write_sales_data(self):
15
201
  yesterday = TimeUtils.get_yesterday()
16
202
  model = SheinStoreSalesDetailManager(self.config.database_url)
@@ -18,12 +204,12 @@ class SheinExcel:
18
204
  data_day = []
19
205
  dict_store_manager_shein = self.config.shein_store_manager
20
206
  dict_store_name = read_dict_from_file(self.config.shein_store_alias)
207
+
208
+ # 准备每日汇总数据
21
209
  for record in records:
22
210
  store_data = []
23
- # store_data.append(record.store_name)
24
211
  store_data.append(dict_store_name.get(record.store_username))
25
212
  store_data.append(dict_store_manager_shein.get(str(record.store_username).lower(), '-'))
26
- # log(dict_store_manager_shein.get(str(record.store_username).lower(),'-'))
27
213
  store_data.append(record.sales_num)
28
214
  store_data.append(record.sales_num_inc)
29
215
  store_data.append(record.sales_amount)
@@ -42,148 +228,36 @@ class SheinExcel:
42
228
  store_data.append(record.upload_product_num_inc)
43
229
  store_data.append(record.sold_out_product_num)
44
230
  store_data.append(record.shelf_off_product_num)
45
- # store_data.append(record.remark) # 不要了
46
231
  data_day.append(store_data)
47
232
 
48
233
  excel_path = create_file_path(self.config.excel_daily_report)
49
234
  delete_file(excel_path)
50
235
  sheet_name_first = 'SHEIN销售部每日店铺情况'
51
- # write_json_to_excel('excel_daily.json',excel_path,sheet_name_first)
52
- app, wb, sheet = open_excel(excel_path, sheet_name_first)
53
- delete_sheet_if_exists(wb, 'Sheet1')
54
-
55
- las_row = len(data_day) + 4
56
- log('len(las_row)', las_row)
57
- sheet.range('B5').value = data_day
58
- # sheet.range('3:3').api.WrapText = True
59
- sheet.range(f'A5:U{las_row}').api.Font.Color = 0x000000 # 红色
60
- sheet.range(f'A5:U{las_row}').api.Font.Bold = False
61
- sheet.range('A1').value = f'销售部SHEIN{TimeUtils.get_current_month()}月店铺数据'
62
- sheet.range('A4').value = f'{TimeUtils.format_date_cross_platform(yesterday)}\n({TimeUtils.get_chinese_weekday(yesterday)})'
63
- sheet.range('A4').column_width = 16
64
- sheet.range('A4').api.VerticalAlignment = -4160 # 垂直顶部对齐
65
- sheet.range(f'A4:A{las_row}').merge()
66
-
67
- # 获取列数据范围
68
- column_range = sheet.range(f'E5:E{las_row}')
69
- # 遍历列中的每个单元格
70
- for cell in column_range:
71
- # 检查单元格是否有值且为负数
72
- if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
73
- # 设置字体颜色为红色
74
- cell.font.color = (255, 0, 0) # RGB值表示红色
75
-
76
- # 获取列数据范围
77
- column_range = sheet.range(f'G5:G{las_row}')
78
- # 遍历列中的每个单元格
79
- for cell in column_range:
80
- # 检查单元格是否有值且为负数
81
- if cell.value is not None and cell.value < 0:
82
- # 设置字体颜色为红色
83
- cell.font.color = (255, 0, 0) # RGB值表示红色
84
-
85
- # 获取列数据范围
86
- column_range = sheet.range(f'I5:I{las_row}')
87
- # 遍历列中的每个单元格
88
- for cell in column_range:
89
- # 检查单元格是否有值且为负数
90
- if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
91
- # 设置字体颜色为红色
92
- cell.font.color = (255, 0, 0) # RGB值表示红色
93
-
94
- # 获取列数据范围
95
- column_range = sheet.range(f'K5:K{las_row}')
96
- # 遍历列中的每个单元格
97
- for cell in column_range:
98
- # 检查单元格是否有值且为负数
99
- if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
100
- # 设置字体颜色为红色
101
- cell.font.color = (255, 0, 0) # RGB值表示红色
102
-
103
- # 处理表头
104
- # 处理第一行
105
- rangeOne = f'A1:U1'
106
- sheet.range(rangeOne).merge()
107
- sheet.range(rangeOne).api.Font.Size = 24
108
- sheet.range(rangeOne).api.Font.Bold = True
109
- sheet.range(rangeOne).api.HorizontalAlignment = -4108
110
- sheet.range(rangeOne).api.VerticalAlignment = -4108
111
-
112
- # 处理第二行
113
- rangeTwo_part_1 = f'A2:C2'
114
- rangeTwo_part_2 = f'D2:O2'
115
- rangeTwo_part_3 = f'P2:U2'
116
- sheet.range(rangeTwo_part_1).merge()
117
- sheet.range(rangeTwo_part_2).merge()
118
- sheet.range(rangeTwo_part_3).merge()
119
236
 
120
- sheet.range(f'A2:C3').color = 0x47a100
121
-
122
- sheet.range('D2').value = f'店铺的结果和稳定性'
123
- sheet.range(rangeTwo_part_2).api.Font.Size = 16
124
- sheet.range(rangeTwo_part_2).api.Font.Color = 0xFFFFFF
125
- sheet.range(rangeTwo_part_2).api.Font.Bold = True
126
- sheet.range(rangeTwo_part_2).api.HorizontalAlignment = -4108
127
- sheet.range(rangeTwo_part_2).api.VerticalAlignment = -4108
128
- sheet.range(f'D2:O3').color = 0x0000FF
129
-
130
- sheet.range('P2').value = f'上新的质量和数量'
131
- sheet.range(rangeTwo_part_3).api.Font.Size = 16
132
- sheet.range(rangeTwo_part_3).api.Font.Color = 0xFFFFFF
133
- sheet.range(rangeTwo_part_3).api.Font.Bold = True
134
- sheet.range(rangeTwo_part_3).api.HorizontalAlignment = -4108
135
- sheet.range(rangeTwo_part_3).api.VerticalAlignment = -4108
136
- sheet.range(f'P2:U3').color = 0x47a100
137
-
138
- # 处理第三行
139
- rangeThree = f'A3:U3'
140
- sheet.range('A3').value = ['日期', '店铺', '店长', '昨日单量', '对比前日', '昨日销售额', '对比前日', '昨日访客',
141
- '对比前天', '备货款A', '对比前日', '新款A', '对比前日', '在售商品', '对比前日', '待上架',
142
- '对比前日', '昨日上传', '对比前日', '已售罄', '已下架']
143
- sheet.range(rangeThree).api.Font.Size = 11
144
- sheet.range(rangeThree).api.Font.Color = 0xFFFFFF
145
- sheet.range(rangeThree).api.Font.Bold = True
146
- sheet.range(rangeThree).api.HorizontalAlignment = -4108
147
- sheet.range(rangeThree).api.VerticalAlignment = -4108
148
-
149
- # 处理第4行
150
- rangeFour = f'B4:U4'
151
- sheet.range('B4').value = '汇总'
152
- sheet.range('C4').value = '-'
153
- sheet.range(rangeFour).api.Font.Size = 11
154
- sheet.range(rangeThree).api.HorizontalAlignment = -4108
155
- sheet.range(rangeThree).api.VerticalAlignment = -4108
156
- sheet.range(f'B4:U4').color = 0x50d092
157
-
158
- for col in range(2, 22): # 对应 C 列到 J 列
159
- col_letter = xw.utils.col_name(col)
160
- if col_letter not in ['A', 'B', 'C']:
161
- sheet.range(f'{col_letter}4').formula = f'=SUM({col_letter}5:{col_letter}{las_row})'
162
- # 所有列水平居中和垂直居中
163
- sheet.range(f'{col_letter}:{col_letter}').api.HorizontalAlignment = -4108
164
- sheet.range(f'{col_letter}:{col_letter}').api.VerticalAlignment = -4108
165
- sheet.autofit()
237
+ # 准备批量操作列表
238
+ operations = []
166
239
 
167
- range_to_border = sheet.range(f'A2:U{las_row}') # 定义范围
168
- # 设置外部边框(所有边都为实线)
169
- range_to_border.api.Borders(7).LineStyle = 1 # 上边框
170
- range_to_border.api.Borders(8).LineStyle = 1 # 下边框
171
- range_to_border.api.Borders(9).LineStyle = 1 # 左边框
172
- range_to_border.api.Borders(10).LineStyle = 1 # 右边框
173
- # 设置内部边框
174
- range_to_border.api.Borders(1).LineStyle = 1 # 内部上边框
175
- range_to_border.api.Borders(2).LineStyle = 1 # 内部下边框
176
- range_to_border.api.Borders(3).LineStyle = 1 # 内部左边框
177
- range_to_border.api.Borders(4).LineStyle = 1 # 内部右边框
240
+ # 添加每日汇总sheet的操作 - 自定义操作函数
241
+ def write_daily_data(sheet):
242
+ # 写入数据到B5位置,保持原有格式
243
+ sheet.range('B5').value = data_day
244
+ # 设置标题
245
+ sheet.range('A1').value = f'销售部SHEIN{TimeUtils.get_current_month()}月店铺数据'
246
+ # 设置日期和合并
247
+ sheet.range('A4').value = f'{TimeUtils.format_date_cross_platform(yesterday)}\n({TimeUtils.get_chinese_weekday(yesterday)})'
178
248
 
179
- wb.save()
180
- close_excel(app, wb)
249
+ operations.append((sheet_name_first, 'format', write_daily_data))
250
+ operations.append((sheet_name_first, 'format', self._format_daily_summary_sheet, yesterday, len(data_day)))
251
+ operations.append(('Sheet1', 'delete'))
252
+ operations.append((sheet_name_first, 'move', 1))
181
253
 
254
+ # 获取店铺列表并准备月度数据
182
255
  store_list = model.get_distinct_store_sales_list()
183
256
  for store in store_list:
184
257
  store_username = store[0]
185
258
  store_name = dict_store_name.get(store_username)
186
259
  records = model.get_one_month_records(TimeUtils.get_current_year(), TimeUtils.get_current_month(), store_username)
260
+
187
261
  data_month = []
188
262
  for record in records:
189
263
  store_data = []
@@ -206,101 +280,228 @@ class SheinExcel:
206
280
  store_data.append(record.upload_product_num_inc)
207
281
  store_data.append(record.sold_out_product_num)
208
282
  store_data.append(record.shelf_off_product_num)
209
- store_data.append(record.remark)
283
+ # store_data.append(record.remark) # 月度数据不包含备注列,保持19列
210
284
  data_month.append(store_data)
211
285
 
212
- excel_path = create_file_path(self.config.excel_daily_report)
213
- sheet_name = store_name
214
- # write_json_to_excel('excel_month.json',excel_path,sheet_name)
215
- las_row = len(data_month) + 4
216
-
217
- app, wb, sheet = open_excel(excel_path, sheet_name)
218
-
219
- # 处理表头
220
- # 处理第一行
221
- rangeOne = f'A1:S1'
222
- sheet.range(rangeOne).merge()
223
- sheet.range(rangeOne).api.Font.Size = 24
224
- sheet.range(rangeOne).api.Font.Bold = True
225
- sheet.range(rangeOne).api.HorizontalAlignment = -4108
226
- sheet.range(rangeOne).api.VerticalAlignment = -4108
227
-
228
- # 处理第二行
229
- rangeTwo_part_1 = f'A2'
230
- rangeTwo_part_2 = f'B2:M2'
231
- rangeTwo_part_3 = f'N2:S2'
232
- sheet.range(rangeTwo_part_2).merge()
233
- sheet.range(rangeTwo_part_3).merge()
234
-
235
- sheet.range(f'A2:A3').color = 0x47a100
236
-
237
- sheet.range('B2').value = f'店铺的结果和稳定性'
238
- sheet.range(rangeTwo_part_2).api.Font.Size = 16
239
- sheet.range(rangeTwo_part_2).api.Font.Color = 0xFFFFFF
240
- sheet.range(rangeTwo_part_2).api.Font.Bold = True
241
- sheet.range(rangeTwo_part_2).api.HorizontalAlignment = -4108
242
- sheet.range(rangeTwo_part_2).api.VerticalAlignment = -4108
243
- sheet.range(f'B2:M3').color = 0x0000FF
244
-
245
- sheet.range('N2').value = f'上新的质量和数量'
246
- sheet.range(rangeTwo_part_3).api.Font.Size = 16
247
- sheet.range(rangeTwo_part_3).api.Font.Color = 0xFFFFFF
248
- sheet.range(rangeTwo_part_3).api.Font.Bold = True
249
- sheet.range(rangeTwo_part_3).api.HorizontalAlignment = -4108
250
- sheet.range(rangeTwo_part_3).api.VerticalAlignment = -4108
251
- sheet.range(f'N2:S3').color = 0x47a100
252
-
253
- # 处理第三行
254
- rangeThree = f'A3:S3'
255
- sheet.range('A3').value = ['日期', '昨日单量', '对比前日', '昨日销售额', '对比前日', '昨日访客', '对比前天',
256
- '备货款A', '对比前日', '新款A', '对比前日', '在售商品', '对比前日', '待上架',
257
- '对比前日', '昨日上传', '对比前日', '已售罄', '已下架']
258
- sheet.range(rangeThree).api.Font.Size = 11
259
- sheet.range(rangeThree).api.Font.Color = 0xFFFFFF
260
- sheet.range(rangeThree).api.Font.Bold = True
261
- sheet.range(rangeThree).api.HorizontalAlignment = -4108
262
- sheet.range(rangeThree).api.VerticalAlignment = -4108
263
-
264
- # 处理第4行
265
- rangeFour = f'A4:S4'
266
- sheet.range('A4').value = '汇总'
267
- sheet.range(rangeFour).api.Font.Size = 11
268
- sheet.range(rangeThree).api.HorizontalAlignment = -4108
269
- sheet.range(rangeThree).api.VerticalAlignment = -4108
270
- sheet.range(f'A4:S4').color = 0x50d092
271
-
272
- # 原逻辑
273
- sheet.range('A5').value = data_month
274
- # sheet.range('3:3').api.WrapText = True
275
- sheet.range('A4:T35').api.Font.Color = 0x000000
276
- sheet.range('A4:T35').api.Font.Bold = False
277
- sheet.range('A1').value = f'{store_name}SHEIN{TimeUtils.get_current_month()}月店铺数据'
278
-
279
- range_to_border = sheet.range(f'A2:S{las_row}') # 定义范围
280
- # 设置外部边框(所有边都为实线)
281
- range_to_border.api.Borders(7).LineStyle = 1 # 上边框
282
- range_to_border.api.Borders(8).LineStyle = 1 # 下边框
283
- range_to_border.api.Borders(9).LineStyle = 1 # 左边框
284
- range_to_border.api.Borders(10).LineStyle = 1 # 右边框
285
- # 设置内部边框
286
- range_to_border.api.Borders(1).LineStyle = 1 # 内部上边框
287
- range_to_border.api.Borders(2).LineStyle = 1 # 内部下边框
288
- range_to_border.api.Borders(3).LineStyle = 1 # 内部左边框
289
- range_to_border.api.Borders(4).LineStyle = 1 # 内部右边框
290
- wb.save()
291
-
292
- for col in range(2, 20): # 对应 C 列到 J 列
293
- col_letter = xw.utils.col_name(col)
294
- # 所有列水平居中和垂直居中
295
- sheet.range(f'{col_letter}:{col_letter}').api.HorizontalAlignment = -4108
296
- sheet.range(f'{col_letter}:{col_letter}').api.VerticalAlignment = -4108
297
- sheet.range(f'{col_letter}4').formula = f'=SUM({col_letter}5:{col_letter}36)'
298
-
299
- if sheet_name_first in [sheet.name for sheet in wb.sheets]:
300
- wb.sheets[sheet_name_first].activate()
301
- wb.save()
302
- close_excel(app, wb)
286
+ # 添加月度sheet操作 - 自定义操作函数
287
+ def write_monthly_data(sheet, data=data_month, name=store_name):
288
+ # 写入数据到A5位置(月度数据从A列开始)
289
+ sheet.range('A5').value = data
290
+ # 设置标题
291
+ sheet.range('A1').value = f'{name}SHEIN{TimeUtils.get_current_month()}月店铺数据'
292
+
293
+ operations.append((store_name, 'format', write_monthly_data))
294
+ operations.append((store_name, 'format', self._format_store_monthly_sheet, store_name, len(data_month)))
295
+
296
+ # 执行批量操作
297
+ operations.append((sheet_name_first, 'active'))
298
+ success = batch_excel_operations(excel_path, operations)
299
+
300
+ if success:
301
+ # 发送文件到企业微信
303
302
  WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
303
+ log(f"销售数据写入完成: {excel_path}")
304
+ else:
305
+ log(f"销售数据写入失败: {excel_path}")
306
+
307
+ def _format_daily_summary_sheet(self, sheet, yesterday, data_length):
308
+ """格式化每日汇总sheet"""
309
+ las_row = data_length + 4 # 数据从第5行开始,4行header
310
+
311
+ # 设置数据区域格式(从B5开始,因为数据写入到B5)
312
+ sheet.range(f'B5:U{las_row}').api.Font.Color = 0x000000
313
+ sheet.range(f'B5:U{las_row}').api.Font.Bold = False
314
+
315
+ # 设置A4日期列的格式和合并
316
+ sheet.range('A4').column_width = 16
317
+ sheet.range('A4').api.VerticalAlignment = -4160 # 垂直顶部对齐
318
+ sheet.range(f'A4:A{las_row}').merge()
319
+
320
+ # 设置负数为红色(E,G,I,K列)
321
+ self._set_negative_numbers_red(sheet, ['E', 'G', 'I', 'K'], 5, las_row)
322
+
323
+ # 格式化表头
324
+ self._format_daily_header(sheet, las_row)
325
+
326
+ # 设置汇总公式和格式
327
+ self._set_summary_formulas(sheet, las_row)
328
+
329
+ # 设置边框
330
+ self._set_borders(sheet, f'A2:U{las_row}')
331
+
332
+ sheet.autofit()
333
+
334
+ def _format_store_monthly_sheet(self, sheet, store_name, data_length):
335
+ """格式化店铺月度sheet"""
336
+ las_row = data_length + 4 # 数据从第5行开始,4行header
337
+
338
+ # 数据已经写入,现在进行格式化
339
+ # 设置数据区域格式(从A5开始到S列,月度数据是19列)
340
+ sheet.range(f'A5:S{las_row}').api.Font.Color = 0x000000
341
+ sheet.range(f'A5:S{las_row}').api.Font.Bold = False
342
+
343
+ # 格式化表头
344
+ self._format_monthly_header(sheet, las_row)
345
+
346
+ # 设置汇总公式和格式
347
+ self._set_monthly_summary_formulas(sheet, las_row)
348
+
349
+ # 设置边框
350
+ self._set_borders(sheet, f'A2:S{las_row}')
351
+
352
+ sheet.autofit()
353
+
354
+ def _set_negative_numbers_red(self, sheet, columns, start_row, end_row):
355
+ """设置负数为红色"""
356
+ for col in columns:
357
+ column_range = sheet.range(f'{col}{start_row}:{col}{end_row}')
358
+ for cell in column_range:
359
+ if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
360
+ cell.font.color = (255, 0, 0)
361
+
362
+ def _format_daily_header(self, sheet, las_row):
363
+ """格式化每日汇总表头,完全按照原始格式"""
364
+ # 第一行:标题
365
+ range_one = f'A1:U1'
366
+ sheet.range(range_one).merge()
367
+ sheet.range(range_one).api.Font.Size = 24
368
+ sheet.range(range_one).api.Font.Bold = True
369
+ sheet.range(range_one).api.HorizontalAlignment = -4108
370
+ sheet.range(range_one).api.VerticalAlignment = -4108
371
+
372
+ # 第二行:分类标题
373
+ range_two_part_1 = f'A2:C2'
374
+ range_two_part_2 = f'D2:O2'
375
+ range_two_part_3 = f'P2:U2'
376
+ sheet.range(range_two_part_1).merge()
377
+ sheet.range(range_two_part_2).merge()
378
+ sheet.range(range_two_part_3).merge()
379
+
380
+ sheet.range(f'A2:C3').color = 0x47a100
381
+
382
+ sheet.range('D2').value = '店铺的结果和稳定性'
383
+ sheet.range(range_two_part_2).api.Font.Size = 16
384
+ sheet.range(range_two_part_2).api.Font.Color = 0xFFFFFF
385
+ sheet.range(range_two_part_2).api.Font.Bold = True
386
+ sheet.range(range_two_part_2).api.HorizontalAlignment = -4108
387
+ sheet.range(range_two_part_2).api.VerticalAlignment = -4108
388
+ sheet.range(f'D2:O3').color = 0x0000FF
389
+
390
+ sheet.range('P2').value = '上新的质量和数量'
391
+ sheet.range(range_two_part_3).api.Font.Size = 16
392
+ sheet.range(range_two_part_3).api.Font.Color = 0xFFFFFF
393
+ sheet.range(range_two_part_3).api.Font.Bold = True
394
+ sheet.range(range_two_part_3).api.HorizontalAlignment = -4108
395
+ sheet.range(range_two_part_3).api.VerticalAlignment = -4108
396
+ sheet.range(f'P2:U3').color = 0x47a100
397
+
398
+ # 第三行:列标题
399
+ range_three = f'A3:U3'
400
+ sheet.range('A3').value = ['日期', '店铺', '店长', '昨日单量', '对比前日', '昨日销售额', '对比前日', '昨日访客',
401
+ '对比前天', '备货款A', '对比前日', '新款A', '对比前日', '在售商品', '对比前日', '待上架',
402
+ '对比前日', '昨日上传', '对比前日', '已售罄', '已下架']
403
+ sheet.range(range_three).api.Font.Size = 11
404
+ sheet.range(range_three).api.Font.Color = 0xFFFFFF
405
+ sheet.range(range_three).api.Font.Bold = True
406
+ sheet.range(range_three).api.HorizontalAlignment = -4108
407
+ sheet.range(range_three).api.VerticalAlignment = -4108
408
+
409
+ # 第四行:汇总行
410
+ range_four = f'B4:U4'
411
+ sheet.range('B4').value = '汇总'
412
+ sheet.range('C4').value = '-'
413
+ sheet.range(range_four).api.Font.Size = 11
414
+ sheet.range(range_four).api.HorizontalAlignment = -4108
415
+ sheet.range(range_four).api.VerticalAlignment = -4108
416
+ sheet.range(f'B4:U4').color = 0x50d092
417
+
418
+ def _format_monthly_header(self, sheet, las_row):
419
+ """格式化月度表头,完全按照原始格式"""
420
+ # 第一行:标题(合并A1:S1)
421
+ range_one = f'A1:S1'
422
+ sheet.range(range_one).merge()
423
+ sheet.range(range_one).api.Font.Size = 24
424
+ sheet.range(range_one).api.Font.Bold = True
425
+ sheet.range(range_one).api.HorizontalAlignment = -4108
426
+ sheet.range(range_one).api.VerticalAlignment = -4108
427
+
428
+ # 第二行:分类标题
429
+ range_two_part_1 = f'A2'
430
+ range_two_part_2 = f'B2:M2'
431
+ range_two_part_3 = f'N2:S2'
432
+ sheet.range(range_two_part_2).merge()
433
+ sheet.range(range_two_part_3).merge()
434
+
435
+ sheet.range(f'A2:A3').color = 0x47a100
436
+
437
+ sheet.range('B2').value = '店铺的结果和稳定性'
438
+ sheet.range(range_two_part_2).api.Font.Size = 16
439
+ sheet.range(range_two_part_2).api.Font.Color = 0xFFFFFF
440
+ sheet.range(range_two_part_2).api.Font.Bold = True
441
+ sheet.range(range_two_part_2).api.HorizontalAlignment = -4108
442
+ sheet.range(range_two_part_2).api.VerticalAlignment = -4108
443
+ sheet.range(f'B2:M3').color = 0x0000FF
444
+
445
+ sheet.range('N2').value = '上新的质量和数量'
446
+ sheet.range(range_two_part_3).api.Font.Size = 16
447
+ sheet.range(range_two_part_3).api.Font.Color = 0xFFFFFF
448
+ sheet.range(range_two_part_3).api.Font.Bold = True
449
+ sheet.range(range_two_part_3).api.HorizontalAlignment = -4108
450
+ sheet.range(range_two_part_3).api.VerticalAlignment = -4108
451
+ sheet.range(f'N2:S3').color = 0x47a100
452
+
453
+ # 第三行:列标题
454
+ range_three = f'A3:S3'
455
+ sheet.range('A3').value = ['日期', '昨日单量', '对比前日', '昨日销售额', '对比前日', '昨日访客', '对比前天',
456
+ '备货款A', '对比前日', '新款A', '对比前日', '在售商品', '对比前日', '待上架',
457
+ '对比前日', '昨日上传', '对比前日', '已售罄', '已下架']
458
+ sheet.range(range_three).api.Font.Size = 11
459
+ sheet.range(range_three).api.Font.Color = 0xFFFFFF
460
+ sheet.range(range_three).api.Font.Bold = True
461
+ sheet.range(range_three).api.HorizontalAlignment = -4108
462
+ sheet.range(range_three).api.VerticalAlignment = -4108
463
+
464
+ # 第四行:汇总行
465
+ range_four = f'A4:S4'
466
+ sheet.range('A4').value = '汇总'
467
+ sheet.range(range_four).api.Font.Size = 11
468
+ sheet.range(range_four).api.HorizontalAlignment = -4108
469
+ sheet.range(range_four).api.VerticalAlignment = -4108
470
+ sheet.range(f'A4:S4').color = 0x50d092
471
+
472
+ def _set_summary_formulas(self, sheet, las_row):
473
+ """设置汇总公式"""
474
+ for col in range(2, 22): # B列到U列(跳过A列日期)
475
+ col_letter = xw.utils.col_name(col)
476
+ if col_letter not in ['A', 'B', 'C']: # A列是日期,B列是汇总,C列是-
477
+ sheet.range(f'{col_letter}4').formula = f'=SUM({col_letter}5:{col_letter}{las_row})'
478
+ # 所有列水平居中和垂直居中
479
+ sheet.range(f'{col_letter}:{col_letter}').api.HorizontalAlignment = -4108
480
+ sheet.range(f'{col_letter}:{col_letter}').api.VerticalAlignment = -4108
481
+
482
+ def _set_monthly_summary_formulas(self, sheet, las_row):
483
+ """设置月度汇总公式"""
484
+ for col in range(2, 20): # B列到S列(对应原始代码的 2 到 20)
485
+ col_letter = xw.utils.col_name(col)
486
+ # 所有列水平居中和垂直居中
487
+ sheet.range(f'{col_letter}:{col_letter}').api.HorizontalAlignment = -4108
488
+ sheet.range(f'{col_letter}:{col_letter}').api.VerticalAlignment = -4108
489
+ # 设置汇总公式(原始代码使用固定的36行)
490
+ sheet.range(f'{col_letter}4').formula = f'=SUM({col_letter}5:{col_letter}36)'
491
+
492
+ def _set_borders(self, sheet, range_str):
493
+ """设置边框"""
494
+ range_to_border = sheet.range(range_str)
495
+ # 设置外部边框
496
+ range_to_border.api.Borders(7).LineStyle = 1 # 上边框
497
+ range_to_border.api.Borders(8).LineStyle = 1 # 下边框
498
+ range_to_border.api.Borders(9).LineStyle = 1 # 左边框
499
+ range_to_border.api.Borders(10).LineStyle = 1 # 右边框
500
+ # 设置内部边框
501
+ range_to_border.api.Borders(1).LineStyle = 1 # 内部上边框
502
+ range_to_border.api.Borders(2).LineStyle = 1 # 内部下边框
503
+ range_to_border.api.Borders(3).LineStyle = 1 # 内部左边框
504
+ range_to_border.api.Borders(4).LineStyle = 1 # 内部右边框
304
505
 
305
506
  def format_bak_advice(self, excel_path, sheet_name, mode):
306
507
  app, wb, sheet = open_excel(excel_path, sheet_name)
@@ -318,9 +519,9 @@ class SheinExcel:
318
519
  add_formula_for_column(sheet, '本地和采购可售天数', '=IF(H2>0, (F2+G2)/H2,0)')
319
520
  add_formula_for_column(sheet, '建议采购', '=IF(I2 > J2,0,E2)')
320
521
 
321
- colorize_by_field(app, wb, sheet, 'SKC')
522
+ colorize_by_field(sheet, 'SKC')
322
523
  specify_column_width(sheet, ['商品信息'], 180 / 6)
323
- InsertImageV2(app, wb, sheet, ['SKC图片', 'SKU图片'])
524
+ InsertImageV2(sheet, ['SKC图片', 'SKU图片'])
324
525
  wb.save()
325
526
  close_excel(app, wb)
326
527
  if mode == 4:
@@ -357,11 +558,11 @@ class SheinExcel:
357
558
  new_excel_path_list.append(new_excel_path)
358
559
  sheet_name = 'Sheet1'
359
560
 
360
- log(new_excel_path)
361
- if mode in [2]:
362
- excel_data = sort_by_column(excel_data, 4, 1)
363
- write_data(new_excel_path, sheet_name, excel_data)
364
- self.format_bak_advice(new_excel_path, sheet_name, mode)
561
+ log(new_excel_path)
562
+ if mode in [2]:
563
+ excel_data = sort_by_column(excel_data, 4, 1)
564
+ write_data(new_excel_path, sheet_name, excel_data)
565
+ self.format_bak_advice(new_excel_path, sheet_name, mode)
365
566
 
366
567
  # 是否合并表格数据
367
568
  if mode in [1, 3]:
@@ -396,7 +597,7 @@ class SheinExcel:
396
597
  beautify_title(sheet)
397
598
  add_borders(sheet)
398
599
  column_to_left(sheet, ['活动信息'])
399
- colorize_by_field(app, wb, sheet, '店铺名称')
600
+ colorize_by_field(sheet, '店铺名称')
400
601
  autofit_column(sheet, ['店铺名称', '活动信息'])
401
602
  wb.save()
402
603
  close_excel(app, wb)
@@ -411,10 +612,10 @@ class SheinExcel:
411
612
 
412
613
  header = []
413
614
  for store_username, excel_data in dict_1.items():
414
- store_name = dict_store.get(store_username)
415
- sheet_name = store_name
416
- write_data(excel_path_1, sheet_name, excel_data)
417
- self.format_jit(excel_path_1, sheet_name)
615
+ # store_name = dict_store.get(store_username)
616
+ # sheet_name = store_name
617
+ # write_data(excel_path_1, sheet_name, excel_data)
618
+ # self.format_jit(excel_path_1, sheet_name)
418
619
  header = excel_data[0]
419
620
  summary_excel_data_1 += excel_data[1:]
420
621
 
@@ -431,10 +632,10 @@ class SheinExcel:
431
632
 
432
633
  header = []
433
634
  for store_username, excel_data in dict_2.items():
434
- store_name = dict_store.get(store_username)
435
- sheet_name = store_name
436
- write_data(excel_path_2, sheet_name, excel_data)
437
- self.format_jit(excel_path_2, sheet_name)
635
+ # store_name = dict_store.get(store_username)
636
+ # sheet_name = store_name
637
+ # write_data(excel_path_2, sheet_name, excel_data)
638
+ # self.format_jit(excel_path_2, sheet_name)
438
639
  header = excel_data[0]
439
640
  summary_excel_data_2 += excel_data[1:]
440
641
 
@@ -447,11 +648,11 @@ class SheinExcel:
447
648
  app, wb, sheet = open_excel(excel_path, sheet_name)
448
649
  beautify_title(sheet)
449
650
  add_borders(sheet)
450
- colorize_by_field(app, wb, sheet, 'SKC')
651
+ colorize_by_field(sheet, 'SKC')
451
652
  column_to_left(sheet, ["商品信息", "近7天SKU销量/SKC销量/SKC曝光", "SKC点击率/SKC转化率", "自主参与活动"])
452
653
  autofit_column(sheet,
453
654
  ['店铺名称', '商品信息', "近7天SKU销量/SKC销量/SKC曝光", "SKC点击率/SKC转化率", "自主参与活动"])
454
- InsertImageV2(app, wb, sheet, ['SKC图片', 'SKU图片'])
655
+ InsertImageV2(sheet, ['SKC图片', 'SKU图片'])
455
656
  wb.save()
456
657
  close_excel(app, wb)
457
658
  WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
@@ -483,12 +684,12 @@ class SheinExcel:
483
684
  format_to_money(sheet, ['申报价', '成本价', '毛利润', '利润'])
484
685
  format_to_percent(sheet, ['支付率', '点击率', '毛利率'])
485
686
  self.dealFormula(sheet) # 有空再封装优化
486
- colorize_by_field(app, wb, sheet, 'SPU')
687
+ colorize_by_field(sheet, 'SPU')
487
688
  autofit_column(sheet, ['商品信息', '店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
488
689
  column_to_left(sheet, ['店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
489
690
  specify_column_width(sheet, ['商品标题'], 150 / 6)
490
691
  add_borders(sheet)
491
- InsertImageV2(app, wb, sheet, ['SKC图片', 'SKU图片'], 'shein', 120, None, None, True)
692
+ InsertImageV2(sheet, ['SKC图片', 'SKU图片'], 'shein', 120, None, None, True)
492
693
  wb.save()
493
694
  close_excel(app, wb)
494
695