qrpa 1.0.34__py3-none-any.whl → 1.1.50__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.
qrpa/shein_excel.py CHANGED
@@ -5,6 +5,7 @@ from .time_utils import TimeUtils
5
5
  from .wxwork import WxWorkBot
6
6
  from .shein_daily_report_model import SheinStoreSalesDetailManager, SheinStoreSalesDetail
7
7
 
8
+ import os
8
9
  import pandas as pd
9
10
  import numpy as np
10
11
 
@@ -15,6 +16,592 @@ class SheinExcel:
15
16
  self.bridge = bridge
16
17
  pass
17
18
 
19
+ def write_sku_not_found(self):
20
+ cache_file = f'{self.config.auto_dir}/shein/dict/sku_not_found.json'
21
+ dict_sku_not_found = read_dict_from_file(cache_file)
22
+
23
+ excel_data = []
24
+ for store_username, data_list in dict_sku_not_found.items():
25
+ excel_data += data_list
26
+
27
+ sheet_name1 = '未匹配SKU_需运营调整'
28
+ operations = [
29
+ [sheet_name1, 'write', [['店铺账户', '店铺别名', '店长', 'SPU', 'SKC', '商家SKC', '商家SKU', '上架状态', '商品层次', '错误原因']] + excel_data],
30
+ [sheet_name1, 'format', self.format_sku_not_found],
31
+ ['Sheet1', 'delete'],
32
+ ]
33
+ cache_file = f'{self.config.auto_dir}/shein/dict/sku_to_skc.json'
34
+ sku_to_skc = read_dict_from_file(cache_file)
35
+ excel_data = []
36
+ for store_username, data_list in sku_to_skc.items():
37
+ excel_data += data_list
38
+
39
+ sheet_name = 'sku到skc映射'
40
+ operations.append([sheet_name, 'write', [['商家SKU', '商家SKC']] + excel_data])
41
+ operations.append([sheet_name, 'format', self.format_sku_to_skc])
42
+
43
+ operations.append([sheet_name1, 'move', 1])
44
+
45
+ batch_excel_operations(self.config.excel_sku_not_found, operations)
46
+
47
+ def format_sku_not_found(self, sheet):
48
+ beautify_title(sheet)
49
+ add_borders(sheet)
50
+ column_to_left(sheet, ['商家SKC', '商家SKU', '商品层次'])
51
+ pass
52
+
53
+ def format_sku_to_skc(self, sheet):
54
+ beautify_title(sheet)
55
+ add_borders(sheet)
56
+ column_to_left(sheet, ['商家SKC', '商家SKU'])
57
+ pass
58
+
59
+ def get_supplier_name(self, store_username):
60
+ cache_file = f'{self.config.auto_dir}/shein/dict/supplier_data.json'
61
+ info = read_dict_from_file_ex(cache_file, store_username)
62
+ return info['supplier_name']
63
+
64
+ def write_withdraw_report_2024(self, year=2024):
65
+ if year == 2025:
66
+ excel_path = create_file_path(self.config.excel_withdraw_2025)
67
+ else:
68
+ excel_path = create_file_path(self.config.excel_withdraw_2024)
69
+ dict_store = read_dict_from_file(self.config.shein_store_alias)
70
+
71
+ header = ['店铺名称', '店铺账号', '供应商名称', '交易单号', '提现时间', '提现成功时间', '更新时间', '提现明细单号',
72
+ '收款帐户', '收款帐户所在地', '净金额', '保证金', '手续费', '汇率', '收款金额', '提现状态']
73
+ summary_excel_data = [header]
74
+ # 先读取提现明细列表写入
75
+ first_day, last_day = TimeUtils.get_year_range_time(year)
76
+ cache_file = f'{self.config.auto_dir}/shein/cache/withdraw_list_{first_day}_{last_day}.json'
77
+ dict_withdraw = read_dict_from_file(cache_file)
78
+ account_list = []
79
+ for store_username, list_withdraw in dict_withdraw.items():
80
+ store_name = dict_store.get(store_username)
81
+ supplier_name = self.get_supplier_name(store_username)
82
+ for withdraw in list_withdraw:
83
+ row_item = []
84
+ row_item.append(store_name)
85
+ row_item.append(store_username)
86
+ row_item.append(supplier_name)
87
+ row_item.append(withdraw['withdrawNo'])
88
+ row_item.append(TimeUtils.convert_timestamp_to_str(withdraw['createTime']))
89
+ row_item.append(TimeUtils.convert_timestamp_to_str(withdraw.get('transferSuccessTime')))
90
+ row_item.append(TimeUtils.convert_timestamp_to_str(withdraw['lastUpdateTime']))
91
+ row_item.append(withdraw['transferNo'])
92
+ account = withdraw['sourceAccountValue']
93
+ if account not in account_list:
94
+ account_list.append(account)
95
+ row_item.append(account)
96
+ row_item.append(withdraw['accountAreaCode'])
97
+ row_item.append(withdraw['netAmount'])
98
+ row_item.append(withdraw['depositAmount'])
99
+ row_item.append(withdraw['commissionAmount'])
100
+ row_item.append(withdraw['exchangeRate'])
101
+ row_item.append(withdraw['receivingAmount'])
102
+ row_item.append(withdraw['withdrawStatusDesc'])
103
+ summary_excel_data.append(row_item)
104
+ sheet_name = '提现明细汇总'
105
+
106
+ operations = [
107
+ [sheet_name, 'write', summary_excel_data, ],
108
+ [sheet_name, 'format', self.format_withdraw_detail]
109
+ ]
110
+
111
+ header = [
112
+ ['收款账户', '总收款金额'],
113
+ ['汇总', ''],
114
+ ]
115
+ summary_excel_data = header
116
+ for account in account_list:
117
+ row_item = []
118
+ row_item.append(account)
119
+ row_item.append('')
120
+ summary_excel_data.append(row_item)
121
+
122
+ sheet_name = f'汇总{year}'
123
+
124
+ operations.append([sheet_name, 'write', summary_excel_data])
125
+ operations.append([sheet_name, 'format', self.format_withdraw_2024])
126
+ operations.append([sheet_name, 'move', 1])
127
+ operations.append(['Sheet1', 'delete'])
128
+
129
+ batch_excel_operations(excel_path, operations)
130
+
131
+ def format_withdraw_detail(self, sheet):
132
+ beautify_title(sheet)
133
+ column_to_right(sheet, ['金额'])
134
+ format_to_money(sheet, ['金额', '保证金', '手续费'])
135
+ format_to_datetime(sheet, ['时间'])
136
+ add_borders(sheet)
137
+
138
+ def format_withdraw_2024(self, sheet):
139
+ beautify_title(sheet)
140
+ column_to_right(sheet, ['金额'])
141
+ format_to_money(sheet, ['金额'])
142
+ format_to_datetime(sheet, ['时间'])
143
+ add_sum_for_cell(sheet, ['总收款金额'])
144
+ add_formula_for_column(sheet, '总收款金额', "=SUMIFS(提现明细汇总!O:O,提现明细汇总!I:I,A3,提现明细汇总!P:P,\"提现成功\")", 3)
145
+ add_borders(sheet)
146
+
147
+ def write_product(self):
148
+ erp = self.config.erp_source
149
+ excel_path = create_file_path(self.config.excel_shein_skc_profit)
150
+ cache_file = f'{self.config.auto_dir}/shein/product/product_{TimeUtils.today_date()}.json'
151
+ dict_product = read_dict_from_file(cache_file)
152
+
153
+ skc_header = ['SKC', '商家SKC', 'SKC图片', '近7天利润', '近30天利润']
154
+ skc_excel_data = []
155
+ dict_skc = []
156
+
157
+ summary_excel_data = []
158
+ header = []
159
+ for store_username, excel_data in dict_product.items():
160
+ header = excel_data[0]
161
+ new_data = []
162
+ for row_item in excel_data[1:]:
163
+ supplier_sku = row_item[5]
164
+ row_item[10] = self.bridge.get_sku_cost(supplier_sku, erp)
165
+ new_data.append(row_item)
166
+
167
+ if row_item[2] not in dict_skc:
168
+ dict_skc.append(row_item[2])
169
+ stat_data = []
170
+ stat_data.append(row_item[2])
171
+ stat_data.append(row_item[3])
172
+ stat_data.append(row_item[4])
173
+ stat_data.append('')
174
+ stat_data.append('')
175
+ skc_excel_data.append(stat_data)
176
+
177
+ summary_excel_data += new_data
178
+
179
+ sheet_name = '商品库'
180
+
181
+ batch_excel_operations(excel_path, [
182
+ (sheet_name, 'write', [header] + summary_excel_data),
183
+ (sheet_name, 'format', self.format_product),
184
+ ])
185
+
186
+ sheet_name = 'Sheet1'
187
+ profit_data = [skc_header] + skc_excel_data
188
+ batch_excel_operations(excel_path, [
189
+ (sheet_name, 'write', sort_by_column(profit_data, 4, 1)),
190
+ (sheet_name, 'format', self.format_profit),
191
+ (sheet_name, 'format', sort_by_column_excel, 'E'),
192
+ ])
193
+
194
+ def write_product_month_analysis(self):
195
+ """写入月度产品分析数据到Excel"""
196
+ excel_path = create_file_path(self.config.excel_shein_skc_profit)
197
+
198
+ # 读取第一个sheet的数据
199
+ cache_file = f'{self.config.auto_dir}/shein/product_analysis/product_analysis_{TimeUtils.today_date()}.json'
200
+ dict_product = read_dict_from_file(cache_file)
201
+
202
+ summary_excel_data = []
203
+ header = []
204
+ for store_username, excel_data in dict_product.items():
205
+ if not header and excel_data:
206
+ header = excel_data[0]
207
+ # 跳过表头,添加所有数据行
208
+ if len(excel_data) > 1:
209
+ summary_excel_data += excel_data[1:]
210
+
211
+ # 读取第二个sheet的数据(每日趋势)
212
+ cache_file2 = f'{self.config.auto_dir}/shein/product_analysis/product_analysis_2_{TimeUtils.today_date()}.json'
213
+ dict_product2 = read_dict_from_file(cache_file2)
214
+
215
+ summary_excel_data2 = []
216
+ header2 = []
217
+ for store_username, excel_data2 in dict_product2.items():
218
+ if not header2 and excel_data2:
219
+ header2 = excel_data2[0]
220
+ # 跳过表头,添加所有数据行
221
+ if len(excel_data2) > 1:
222
+ summary_excel_data2 += excel_data2[1:]
223
+
224
+ sheet_name = '月度商品分析'
225
+ sheet_name2 = 'SKC每日趋势'
226
+
227
+ # 分开处理两个sheet,避免操作冲突
228
+ # 第一个sheet:月度商品分析
229
+ batch_excel_operations(excel_path, [
230
+ (sheet_name, 'write', [header] + summary_excel_data),
231
+ (sheet_name, 'format', self.format_product_month_analysis),
232
+ (sheet_name, 'move', 1),
233
+ ])
234
+
235
+ # 第二个sheet:SKC每日趋势(独立操作)
236
+ batch_excel_operations(excel_path, [
237
+ (sheet_name2, 'write', [header2] + summary_excel_data2),
238
+ (sheet_name2, 'format', self.format_product_month_analysis_trend),
239
+ ('Sheet1', 'delete'),
240
+ ])
241
+
242
+ def format_product_month_analysis(self, sheet):
243
+ """格式化月度商品分析表格"""
244
+ # 合并相同SKC的单元格
245
+ merge_by_column_v2(sheet, 'skc', ['店铺信息', '商品信息', 'SKC图片', '30天SKC曝光', '30天SKC点击率', '30天SKC转化率', '评论数', '差评率', '客单退货件数'])
246
+
247
+ # 美化表头
248
+ beautify_title(sheet)
249
+
250
+ # 添加边框
251
+ add_borders(sheet)
252
+
253
+ # 设置列格式
254
+ format_to_money(sheet, ['销售额', '核价', '成本', '30天利润'])
255
+ format_to_percent(sheet, ['30天利润率', '30天SKC点击率', '30天SKC转化率', '差评率'])
256
+ column_to_right(sheet, ['销售额', '核价', '成本', '30天利润'])
257
+
258
+ # 设置列宽和对齐
259
+ specify_column_width(sheet, ['店铺信息', '商品信息', 'SKU信息'], 160 / 6)
260
+ column_to_left(sheet, ['店铺信息', '商品信息', 'SKU信息'])
261
+ autofit_column(sheet, ['商品信息', 'SKU信息'])
262
+
263
+ # 添加公式
264
+ # 销售额 (M列) = SKU30天销量 (L列) * 核价 (N列)
265
+ add_formula_for_column(sheet, '销售额', '=IF(AND(ISNUMBER(L2), ISNUMBER(N2)), L2*N2, 0)')
266
+ # 30天利润 (P列) = SKU30天销量 (L列) * (核价 (N列) - 成本 (O列))
267
+ add_formula_for_column(sheet, '30天利润', '=IF(AND(ISNUMBER(L2), ISNUMBER(N2), ISNUMBER(O2)), L2*(N2-O2), 0)')
268
+ # 30天利润率 (Q列) = 30天利润 (P列) / 销售额 (M列)
269
+ add_formula_for_column(sheet, '30天利润率', '=IF(AND(ISNUMBER(M2), M2<>0), P2/M2, 0)')
270
+
271
+ # 插入图片(使用V3一次性插入多列,避免图片被清空)
272
+ InsertImageV3(sheet, ['SKC图片', 'SKU图片'], 'shein', [90, 60])
273
+
274
+ # 按SKC着色(改进版,正确处理合并单元格)
275
+ colorize_by_field_v2(sheet, 'skc')
276
+
277
+ def format_product_month_analysis_trend(self, sheet):
278
+ """格式化SKC每日趋势表格"""
279
+
280
+ # 合并相同SKC的单元格
281
+ merge_by_column_v2(sheet, 'skc', ['店铺信息', '商品信息', 'SKC图片'])
282
+
283
+ # 美化表头
284
+ beautify_title(sheet)
285
+
286
+ # 添加边框
287
+ add_borders(sheet)
288
+
289
+ # 设置列格式
290
+ format_to_date(sheet, ['日期'])
291
+ format_to_percent(sheet, ['SKC点击率', 'SKC转化率'])
292
+
293
+ # 设置列宽和对齐
294
+ specify_column_width(sheet, ['店铺信息', '商品信息'], 160 / 6)
295
+ column_to_left(sheet, ['店铺信息', '商品信息'])
296
+ autofit_column(sheet, ['商品信息'])
297
+
298
+ # 插入图片
299
+ InsertImageV2(sheet, ['SKC图片'], 'shein', 90)
300
+
301
+ # 按SKC着色(改进版,正确处理合并单元格)
302
+ colorize_by_field_v2(sheet, 'skc')
303
+
304
+ def format_profit(self, sheet):
305
+ beautify_title(sheet)
306
+ add_borders(sheet)
307
+ format_to_money(sheet, ['成本价', '核价', '利润'])
308
+ column_to_right(sheet, ['成本价', '核价', '利润'])
309
+ add_formula_for_column(sheet, '近7天利润', '=SUMIFS(商品库!L:L,商品库!P:P,A2)')
310
+ add_formula_for_column(sheet, '近30天利润', '=SUMIFS(商品库!M:M,商品库!P:P,A2)')
311
+ InsertImageV2(sheet, ['SKC图片'], 'shein', 90)
312
+
313
+ def format_product(self, sheet):
314
+ merge_by_column_v2(sheet, 'SPU', ['店铺信息', '产品信息'])
315
+ merge_by_column_v2(sheet, 'SKC', ['SKC图片', '商家SKC'])
316
+ beautify_title(sheet)
317
+ add_borders(sheet)
318
+ format_to_datetime(sheet, ['时间'])
319
+ format_to_money(sheet, ['成本价', '核价', '利润'])
320
+ column_to_right(sheet, ['成本价', '核价', '利润'])
321
+ autofit_column(sheet, ['产品信息'])
322
+ column_to_left(sheet, ['产品信息', '商家SKU', '商家SKC', '属性集'])
323
+ specify_column_width(sheet, ['店铺信息', '产品信息', '属性集'], 160 / 6)
324
+ specify_column_width(sheet, ['商家SKU', '商家SKC'], 220 / 6)
325
+ add_formula_for_column(sheet, '近7天利润', '=IF(ISNUMBER(K2), H2*(J2-K2),0)')
326
+ add_formula_for_column(sheet, '近30天利润', '=IF(ISNUMBER(K2), I2*(J2-K2),0)')
327
+ InsertImageV2(sheet, ['SKC图片'], 'shein', 150, '商家SKC', 'shein_skc_img')
328
+
329
+ def write_week_ntb(self):
330
+ excel_path = create_file_path(self.config.excel_week_report)
331
+
332
+ cache_file = f'{self.config.auto_dir}/shein/dict/new_product_to_bak_{TimeUtils.today_date()}.json'
333
+ dict = read_dict_from_file(cache_file)
334
+ # dict_store = read_dict_from_file(config.dict_store_cache)
335
+
336
+ summary_excel_data = []
337
+ header = []
338
+ dict_store_bak_stat = {}
339
+ for store_username, excel_data in dict.items():
340
+ # store_name = dict_store.get(store_username)
341
+ if dict_store_bak_stat.get(store_username) is None:
342
+ dict_store_bak_stat[store_username] = [0, 0]
343
+ for item in excel_data[1:]:
344
+ dict_store_bak_stat[store_username][0] += 1
345
+ if int(item[6]) == 1:
346
+ dict_store_bak_stat[store_username][1] += 1
347
+ header = excel_data[0]
348
+ summary_excel_data += excel_data[1:]
349
+ summary_excel_data = [header] + summary_excel_data
350
+ log(summary_excel_data)
351
+ sheet_name = '新品转备货款明细'
352
+
353
+ # write_data(excel_path, sheet_name, summary_excel_data)
354
+ # self.format_week_ntb(excel_path, sheet_name)
355
+
356
+ batch_excel_operations(excel_path, [
357
+ (sheet_name, 'write', summary_excel_data),
358
+ (sheet_name, 'format', self.format_week_ntb),
359
+ ])
360
+
361
+ dict_key = f'{self.config.auto_dir}/shein/dict/dict_store_bak_stat_{TimeUtils.today_date()}.json'
362
+ write_dict_to_file(dict_key, dict_store_bak_stat)
363
+
364
+ def format_week_ntb(self, sheet):
365
+ beautify_title(sheet)
366
+ format_to_date(sheet, ['统计日期'])
367
+ format_to_percent(sheet, ['占比'])
368
+ colorize_by_field(sheet, 'SPU')
369
+ column_to_left(sheet, ['商品信息', '第4周SKC点击率/SKC转化率', '第4周SKC销量/SKC曝光'])
370
+ autofit_column(sheet, ['店铺名称', '商品信息', '第4周SKC点击率/SKC转化率', '第4周SKC销量/SKC曝光'])
371
+ add_borders(sheet)
372
+ InsertImageV2(sheet, ['SKC图片'], 'shein', 120)
373
+
374
+ def dealFundsExcelFormat(self, sheet):
375
+ col_a = find_column_by_data(sheet, 1, '店铺名称')
376
+ col_b = find_column_by_data(sheet, 1, '在途商品金额')
377
+ col_c = find_column_by_data(sheet, 1, '在仓商品金额')
378
+ col_d = find_column_by_data(sheet, 1, '待结算金额')
379
+ col_e = find_column_by_data(sheet, 1, '可提现金额')
380
+ col_f = find_column_by_data(sheet, 1, '汇总')
381
+ col_g = find_column_by_data(sheet, 1, '导出时间')
382
+ col_h = find_column_by_data(sheet, 1, '销售出库金额')
383
+
384
+ sheet.range(f'{col_a}:{col_a}').column_width = 25
385
+ sheet.range(f'{col_g}:{col_g}').number_format = 'yyyy-mm-dd hh:mm:ss'
386
+
387
+ last_row = sheet.range('A' + str(sheet.cells.last_cell.row)).end('up').row
388
+ sheet.range(f'{col_b}2').formula = f'=SUM({col_b}3:{col_b}{last_row})'
389
+ sheet.range(f'{col_b}2').number_format = '¥#,##0.00'
390
+ cell = sheet.range(f'{col_b}2')
391
+ cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
392
+ cell.api.Font.Bold = True
393
+
394
+ sheet.range(f'{col_c}2').formula = f'=SUM({col_c}3:{col_c}{last_row})'
395
+ sheet.range(f'{col_c}2').number_format = '¥#,##0.00'
396
+ cell = sheet.range(f'{col_c}2')
397
+ cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
398
+ cell.api.Font.Bold = True
399
+
400
+ sheet.range(f'{col_d}2').formula = f'=SUM({col_d}3:{col_d}{last_row})'
401
+ sheet.range(f'{col_d}2').number_format = '¥#,##0.00'
402
+ cell = sheet.range(f'{col_d}2')
403
+ cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
404
+ cell.api.Font.Bold = True
405
+
406
+ sheet.range(f'{col_e}2').formula = f'=SUM({col_e}3:{col_e}{last_row})'
407
+ sheet.range(f'{col_e}2').number_format = '¥#,##0.00'
408
+ cell = sheet.range(f'{col_e}2')
409
+ cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
410
+ cell.api.Font.Bold = True
411
+
412
+ sheet.range(f'{col_h}2').formula = f'=SUM({col_h}3:{col_h}{last_row})'
413
+ sheet.range(f'{col_h}2').number_format = '¥#,##0.00'
414
+ cell = sheet.range(f'{col_h}2')
415
+ cell.api.Font.Color = 255 # RGB(255, 0, 0),红色对应的颜色代码
416
+ cell.api.Font.Bold = True
417
+
418
+ # 遍历可用行
419
+ used_range_row = sheet.range('A1').expand('down')
420
+ for i, cell in enumerate(used_range_row):
421
+ row = i + 1
422
+ if row < 2:
423
+ continue
424
+ # 设置数字格式
425
+ sheet.range(f'{col_b}{row}').number_format = '¥#,##0.00'
426
+ sheet.range(f'{col_c}{row}').number_format = '¥#,##0.00'
427
+ sheet.range(f'{col_d}{row}').number_format = '¥#,##0.00'
428
+ sheet.range(f'{col_e}{row}').number_format = '¥#,##0.00'
429
+ sheet.range(f'{col_f}{row}').formula = f'=SUM({col_b}{row}:{col_e}{row})'
430
+ sheet.range(f'{col_f}{row}').number_format = '¥#,##0.00'
431
+ sheet.range(f'{col_f}{row}').api.Font.Color = 255
432
+ sheet.range(f'{col_f}{row}').api.Font.Bold = True
433
+
434
+ add_borders(sheet)
435
+
436
+ def write_week_finance_report(self):
437
+ cache_file = f'{self.config.auto_dir}/shein/cache/stat_fund_lz_{TimeUtils.today_date()}.json'
438
+ dict = read_dict_from_file(cache_file)
439
+ dict_key = f'{self.config.auto_dir}/shein/dict/dict_store_bak_stat_{TimeUtils.today_date()}.json'
440
+ dict_store_bak_stat = read_dict_from_file(dict_key)
441
+ data = []
442
+ for key, val in dict.items():
443
+ data.append(val)
444
+ log(data)
445
+ for item in data:
446
+ store_username = item[1]
447
+ item[9] = dict_store_bak_stat[store_username][0]
448
+ item[10] = dict_store_bak_stat[store_username][1]
449
+
450
+ data.sort(key=lambda row: row[8], reverse=True)
451
+ excel_path = create_file_path(self.config.excel_week_report)
452
+ sheet_name = '按店铺汇总'
453
+
454
+ date_A = f'新品上架数量\n({TimeUtils.get_past_nth_day(29, TimeUtils.get_month_first_day())},{TimeUtils.get_past_nth_day(29, TimeUtils.get_yesterday())})'
455
+ date_B = f'成功转备货款\n({TimeUtils.get_month_first_day()},{TimeUtils.get_yesterday()})'
456
+
457
+ data.insert(0, ['汇总', '', '', '', '', '', '', '', '', '', '', '', ''])
458
+ data.insert(0, ['店铺名称', '店铺账号', '店长', '在途商品金额', '在仓商品金额', '待结算金额', '可提现金额', '汇总',
459
+ '销售出库金额', date_A, date_B, '成功率', '导出时间'])
460
+ write_data(excel_path, sheet_name, data)
461
+ app, wb, sheet = open_excel(excel_path, sheet_name)
462
+ beautify_title(sheet)
463
+
464
+ self.dealFundsExcelFormat(sheet)
465
+ format_to_percent(sheet, ['成功率'], 0)
466
+ add_formula_for_column(sheet, '成功率', '=IF(J2=0, 0, k2/J2)', 2)
467
+ add_formula_for_column(sheet, date_A, "=COUNTIF('新品转备货款明细'!A:A, B3)", 3)
468
+ add_formula_for_column(sheet, date_B, "=COUNTIFS('新品转备货款明细'!A:A, B3, '新品转备货款明细'!G:G, 1)", 3)
469
+ add_sum_for_cell(sheet, [date_A, date_B])
470
+ column_to_right(sheet, ['金额', '汇总'])
471
+ sheet.autofit()
472
+ autofit_column(sheet, [date_A, date_B])
473
+ delete_sheet_if_exists(wb, 'Sheet1')
474
+ wb.save()
475
+ close_excel(app, wb)
476
+
477
+ new_data = data
478
+ new_data = aggregate_by_column(new_data, '店长')
479
+ new_data_sorted = new_data[2:]
480
+ new_data_sorted.sort(key=lambda row: row[8], reverse=True)
481
+
482
+ sheet_name = '按店长汇总'
483
+ write_data(excel_path, sheet_name, data[:2] + new_data_sorted)
484
+ app, wb, sheet = open_excel(excel_path, sheet_name)
485
+ add_borders(sheet)
486
+ format_to_money(sheet, ['金额', '成本'])
487
+ format_to_datetime(sheet, ['时间'])
488
+ format_to_percent(sheet, ['成功率'], 0)
489
+ add_formula_for_column(sheet, '成功率', '=IF(J2=0, 0, k2/J2)', 2)
490
+ # 聚合的不能使用这种公式
491
+ # add_formula_for_column(sheet, '新品上架数量',"=COUNTIF('新品转备货款明细'!A:A, B3)",3)
492
+ # add_formula_for_column(sheet, '成功转备货款',"=COUNTIFS('新品转备货款明细'!A:A, B3, '新品转备货款明细'!G:G, 1)",3)
493
+ add_sum_for_cell(sheet, ['在途商品金额', '在仓商品金额', '待结算金额', '可提现金额', '汇总', '销售出库金额',
494
+ date_A, date_B])
495
+ clear_for_cell(sheet, ['店铺账号', '导出时间'])
496
+ add_formula_for_column(sheet, '汇总', f'=SUM(D3:G3)', 3)
497
+ set_title_style(sheet)
498
+ column_to_right(sheet, ['金额', '汇总'])
499
+ # sheet.autofit()
500
+ autofit_column(sheet, ['店铺名称', '店铺账号', date_A, date_B, '导出时间'])
501
+ wb.save()
502
+ close_excel(app, wb)
503
+ WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
504
+
505
+ def write_return_list_range(self, erp, start_date, end_date):
506
+ header = ['退货单号', '退货出库时间', '签收状态', '店铺信息', '店长', '退货类型', '退货原因', 'SKC图片', 'SKC信息', '商家SKU', '属性集', 'SKU退货数量', '平台SKU', 'ERP默认供货商', 'ERP成本', '包裹名', '包裹号', '退货计划单号', '订单号', '发货单', '退回方式', '快递名称', '运单号', '退货地址', '商家联系人', '商家手机号', '入库问题图片地址']
507
+ excel_data = [header]
508
+
509
+ dict_store = read_dict_from_file(self.config.shein_store_alias)
510
+
511
+ cache_file = f'{self.config.auto_dir}/shein/cache/shein_return_order_list_{start_date}_{end_date}.json'
512
+ dict = read_dict_from_file(cache_file)
513
+ for store_username, shein_back_list in dict.items():
514
+ for item in shein_back_list:
515
+ store_name = dict_store.get(store_username)
516
+
517
+ returnOrderId = item['id']
518
+ cache_file = f'{self.config.auto_dir}/shein/cache/shein_return_order_box_detail_{returnOrderId}.json'
519
+ return_detail = read_dict_from_file(cache_file)
520
+ if len(return_detail) == 0:
521
+ continue
522
+
523
+ returnOrderNo = item['returnOrderNo']
524
+ returnOrderTypeName = item['returnOrderTypeName']
525
+ returnOrderStatusName = item['returnOrderStatusName']
526
+ returnReasonTypeName = item['returnReasonTypeName']
527
+ returnReason = item['returnReason']
528
+ waitReturnQuantity = item['waitReturnQuantity']
529
+ skcReturnQuantity = item['returnQuantity']
530
+ returnAmount = item['returnAmount']
531
+ currencyCode = item['currencyCode']
532
+ returnPlanNo = item['returnPlanNo']
533
+ sellerOrderNo = item['sellerOrderNo']
534
+ sellerDeliveryNo = item['sellerDeliveryNo']
535
+ completeTime = item['completeTime']
536
+ returnWayTypeName = item['returnWayTypeName']
537
+ returnExpressCompanyName = item['returnExpressCompanyName']
538
+ expressNoList = item['expressNoList']
539
+ returnAddress = item['returnAddress']
540
+ sellerContract = item['sellerContract']
541
+ sellerContractPhone = item['sellerContractPhone']
542
+ isSign = ['已报废', '已签收', '待签收'][item['isSign']]
543
+ if item['returnScrapType'] == 1:
544
+ urls = item.get('qc_report_url', '-')
545
+ else:
546
+ urls = '\n'.join(item['rejectPicUrlList'])
547
+
548
+ for box_list in return_detail:
549
+ for package_list in box_list['boxList']:
550
+ package_name = package_list['packageName']
551
+ package_no = package_list['returnBoxNo']
552
+ for skc_item in package_list['goods']:
553
+ skc_img = skc_item['imgPath']
554
+ skc = skc_item['skc']
555
+ supplierCode = skc_item['supplierCode']
556
+ for sku_item in skc_item['details']:
557
+ platformSku = sku_item['platformSku']
558
+ supplierSku = sku_item['supplierSku']
559
+ suffixZh = sku_item['suffixZh']
560
+ returnQuantity = sku_item['returnQuantity']
561
+
562
+ store_info = f'{store_username}\n{store_name}\n处理类型: {returnOrderTypeName}\n退货状态: {returnOrderStatusName}'
563
+ skc_info = f'SKC: {skc}\n供方货号: {supplierCode}\n预计退货数量/执行退货数量: {waitReturnQuantity}/{skcReturnQuantity}\n预计退货货值: {returnAmount} {currencyCode}'
564
+
565
+ row_item = []
566
+ row_item.append(returnOrderNo)
567
+ row_item.append(completeTime)
568
+ row_item.append(isSign)
569
+ row_item.append(store_info)
570
+ row_item.append(self.config.shein_store_manager.get(str(store_username).lower()))
571
+ row_item.append(returnReasonTypeName)
572
+ row_item.append(returnReason)
573
+ row_item.append(skc_img)
574
+ row_item.append(skc_info)
575
+ row_item.append(supplierSku)
576
+ row_item.append(suffixZh)
577
+ row_item.append(returnQuantity)
578
+ row_item.append(platformSku)
579
+ row_item.append(self.bridge.get_sku_supplier(supplierSku, erp))
580
+ row_item.append(self.bridge.get_sku_cost(supplierSku, erp))
581
+ row_item.append(package_name)
582
+ row_item.append(package_no)
583
+ row_item.append(returnPlanNo)
584
+ row_item.append(sellerOrderNo)
585
+ row_item.append(sellerDeliveryNo)
586
+ row_item.append(returnWayTypeName)
587
+ row_item.append(returnExpressCompanyName)
588
+ row_item.append(expressNoList)
589
+ row_item.append(returnAddress)
590
+ row_item.append(sellerContract)
591
+ row_item.append(sellerContractPhone)
592
+ row_item.append(urls)
593
+
594
+ excel_data.append(row_item)
595
+
596
+ cache_file_excel = f'{self.config.auto_dir}/shein/cache/shein_return_order_list_excel_{start_date}_{end_date}.json'
597
+ write_dict_to_file(cache_file_excel, excel_data)
598
+
599
+ sheet_name = '希音退货列表'
600
+ batch_excel_operations(self.config.excel_return_list, [
601
+ (sheet_name, 'write', excel_data, ['W', 'Z']),
602
+ (sheet_name, 'format', self.format_return_list)
603
+ ])
604
+
18
605
  # 退货列表
19
606
  def write_return_list(self, erp, start_date, end_date):
20
607
  header = ['退货单号', '退货出库时间', '签收状态', '店铺信息', '店长', '退货类型', '退货原因', 'SKC图片', 'SKC信息', '商家SKU', '属性集', 'SKU退货数量', '平台SKU', 'ERP默认供货商', 'ERP成本', '包裹名', '包裹号', '退货计划单号', '订单号', '发货单', '退回方式', '快递名称', '运单号', '退货地址', '商家联系人', '商家手机号', '入库问题图片地址']
@@ -26,7 +613,6 @@ class SheinExcel:
26
613
  dict = read_dict_from_file(cache_file)
27
614
  for store_username, shein_back_list in dict.items():
28
615
  for item in shein_back_list:
29
-
30
616
  store_name = dict_store.get(store_username)
31
617
 
32
618
  returnOrderId = item['id']
@@ -60,61 +646,68 @@ class SheinExcel:
60
646
  else:
61
647
  urls = '\n'.join(item['rejectPicUrlList'])
62
648
 
63
- for package_list in return_detail[0]['boxList']:
64
- package_name = package_list['packageName']
65
- package_no = package_list['returnBoxNo']
66
- for skc_item in package_list['goods']:
67
- skc_img = skc_item['imgPath']
68
- skc = skc_item['skc']
69
- supplierCode = skc_item['supplierCode']
70
- for sku_item in skc_item['details']:
71
- platformSku = sku_item['platformSku']
72
- supplierSku = sku_item['supplierSku']
73
- suffixZh = sku_item['suffixZh']
74
- returnQuantity = sku_item['returnQuantity']
75
-
76
- store_info = f'{store_username}\n{store_name}\n处理类型: {returnOrderTypeName}\n退货状态: {returnOrderStatusName}'
77
- skc_info = f'SKC: {skc}\n供方货号: {supplierCode}\n预计退货数量/执行退货数量: {waitReturnQuantity}/{skcReturnQuantity}\n预计退货货值: {returnAmount} {currencyCode}'
78
-
79
- row_item = []
80
- row_item.append(returnOrderNo)
81
- row_item.append(completeTime)
82
- row_item.append(isSign)
83
- row_item.append(store_info)
84
- row_item.append(self.config.shein_store_manager.get(str(store_username).lower()))
85
- row_item.append(returnReasonTypeName)
86
- row_item.append(returnReason)
87
- row_item.append(skc_img)
88
- row_item.append(skc_info)
89
- row_item.append(supplierSku)
90
- row_item.append(suffixZh)
91
- row_item.append(returnQuantity)
92
- row_item.append(platformSku)
93
- row_item.append(self.bridge.get_sku_supplier(supplierSku, erp))
94
- row_item.append(self.bridge.get_sku_cost(supplierSku, erp))
95
- row_item.append(package_name)
96
- row_item.append(package_no)
97
- row_item.append(returnPlanNo)
98
- row_item.append(sellerOrderNo)
99
- row_item.append(sellerDeliveryNo)
100
- row_item.append(returnWayTypeName)
101
- row_item.append(returnExpressCompanyName)
102
- row_item.append(expressNoList)
103
- row_item.append(returnAddress)
104
- row_item.append(sellerContract)
105
- row_item.append(sellerContractPhone)
106
- row_item.append(urls)
107
-
108
- excel_data.append(row_item)
649
+ for box_list in return_detail:
650
+ for package_list in box_list['boxList']:
651
+ package_name = package_list['packageName']
652
+ package_no = package_list['returnBoxNo']
653
+ for skc_item in package_list['goods']:
654
+ skc_img = skc_item['imgPath']
655
+ skc = skc_item['skc']
656
+ supplierCode = skc_item['supplierCode']
657
+ for sku_item in skc_item['details']:
658
+ platformSku = sku_item['platformSku']
659
+ supplierSku = sku_item['supplierSku']
660
+ suffixZh = sku_item['suffixZh']
661
+ returnQuantity = sku_item['returnQuantity']
662
+
663
+ store_info = f'{store_username}\n{store_name}\n处理类型: {returnOrderTypeName}\n退货状态: {returnOrderStatusName}'
664
+ skc_info = f'SKC: {skc}\n供方货号: {supplierCode}\n预计退货数量/执行退货数量: {waitReturnQuantity}/{skcReturnQuantity}\n预计退货货值: {returnAmount} {currencyCode}'
665
+
666
+ row_item = []
667
+ row_item.append(returnOrderNo)
668
+ row_item.append(completeTime)
669
+ row_item.append(isSign)
670
+ row_item.append(store_info)
671
+ row_item.append(self.config.shein_store_manager.get(str(store_username).lower()))
672
+ row_item.append(returnReasonTypeName)
673
+ row_item.append(returnReason)
674
+ row_item.append(skc_img)
675
+ row_item.append(skc_info)
676
+ row_item.append(supplierSku)
677
+ row_item.append(suffixZh)
678
+ row_item.append(returnQuantity)
679
+ row_item.append(platformSku)
680
+ row_item.append(self.bridge.get_sku_supplier(supplierSku, erp))
681
+ row_item.append(self.bridge.get_sku_cost(supplierSku, erp))
682
+ row_item.append(package_name)
683
+ row_item.append(package_no)
684
+ row_item.append(returnPlanNo)
685
+ row_item.append(sellerOrderNo)
686
+ row_item.append(sellerDeliveryNo)
687
+ row_item.append(returnWayTypeName)
688
+ row_item.append(returnExpressCompanyName)
689
+ row_item.append(expressNoList)
690
+ row_item.append(returnAddress)
691
+ row_item.append(sellerContract)
692
+ row_item.append(sellerContractPhone)
693
+ row_item.append(urls)
694
+
695
+ excel_data.append(row_item)
109
696
 
110
697
  cache_file_excel = f'{self.config.auto_dir}/shein/cache/shein_return_order_list_excel_{start_date}_{end_date}.json'
111
698
  write_dict_to_file(cache_file_excel, excel_data)
112
699
 
700
+ # sheet_name = '希音退货列表'
701
+ # batch_excel_operations(self.config.excel_return_list, [
702
+ # (sheet_name, 'write', excel_data, ['W', 'Z']),
703
+ # (sheet_name, 'format', self.format_return_list)
704
+ # ])
705
+
706
+ excel_data = [header]
113
707
  cache_file = f'{self.config.auto_dir}/shein/cache/shein_return_order_list_{TimeUtils.today_date()}.json'
114
708
  dict = read_dict_from_file(cache_file)
115
709
  for store_username, shein_back_list in dict.items():
116
710
  for item in shein_back_list:
117
-
118
711
  store_name = dict_store.get(store_username)
119
712
 
120
713
  returnOrderId = item['id']
@@ -148,52 +741,53 @@ class SheinExcel:
148
741
  else:
149
742
  urls = '\n'.join(item['rejectPicUrlList'])
150
743
 
151
- for package_list in return_detail[0]['boxList']:
152
- package_name = package_list['packageName']
153
- package_no = package_list['returnBoxNo']
154
- for skc_item in package_list['goods']:
155
- skc_img = skc_item['imgPath']
156
- skc = skc_item['skc']
157
- supplierCode = skc_item['supplierCode']
158
- for sku_item in skc_item['details']:
159
- platformSku = sku_item['platformSku']
160
- supplierSku = sku_item['supplierSku']
161
- suffixZh = sku_item['suffixZh']
162
- returnQuantity = sku_item['returnQuantity']
163
-
164
- store_info = f'{store_username}\n{store_name}\n处理类型: {returnOrderTypeName}\n退货状态: {returnOrderStatusName}'
165
- skc_info = f'SKC: {skc}\n供方货号: {supplierCode}\n预计退货数量/执行退货数量: {waitReturnQuantity}/{skcReturnQuantity}\n预计退货货值: {returnAmount} {currencyCode}'
166
-
167
- row_item = []
168
- row_item.append(returnOrderNo)
169
- row_item.append(completeTime)
170
- row_item.append(isSign)
171
- row_item.append(store_info)
172
- row_item.append(self.config.shein_store_manager.get(str(store_username).lower()))
173
- row_item.append(returnReasonTypeName)
174
- row_item.append(returnReason)
175
- row_item.append(skc_img)
176
- row_item.append(skc_info)
177
- row_item.append(supplierSku)
178
- row_item.append(suffixZh)
179
- row_item.append(returnQuantity)
180
- row_item.append(platformSku)
181
- row_item.append(self.bridge.get_sku_supplier(supplierSku, erp))
182
- row_item.append(self.bridge.get_sku_cost(supplierSku, erp))
183
- row_item.append(package_name)
184
- row_item.append(package_no)
185
- row_item.append(returnPlanNo)
186
- row_item.append(sellerOrderNo)
187
- row_item.append(sellerDeliveryNo)
188
- row_item.append(returnWayTypeName)
189
- row_item.append(returnExpressCompanyName)
190
- row_item.append(expressNoList)
191
- row_item.append(returnAddress)
192
- row_item.append(sellerContract)
193
- row_item.append(sellerContractPhone)
194
- row_item.append(urls)
195
-
196
- excel_data.append(row_item)
744
+ for box_list in return_detail:
745
+ for package_list in box_list['boxList']:
746
+ package_name = package_list['packageName']
747
+ package_no = package_list['returnBoxNo']
748
+ for skc_item in package_list['goods']:
749
+ skc_img = skc_item['imgPath']
750
+ skc = skc_item['skc']
751
+ supplierCode = skc_item['supplierCode']
752
+ for sku_item in skc_item['details']:
753
+ platformSku = sku_item['platformSku']
754
+ supplierSku = sku_item['supplierSku']
755
+ suffixZh = sku_item['suffixZh']
756
+ returnQuantity = sku_item['returnQuantity']
757
+
758
+ store_info = f'{store_username}\n{store_name}\n处理类型: {returnOrderTypeName}\n退货状态: {returnOrderStatusName}'
759
+ skc_info = f'SKC: {skc}\n供方货号: {supplierCode}\n预计退货数量/执行退货数量: {waitReturnQuantity}/{skcReturnQuantity}\n预计退货货值: {returnAmount} {currencyCode}'
760
+
761
+ row_item = []
762
+ row_item.append(returnOrderNo)
763
+ row_item.append(completeTime)
764
+ row_item.append(isSign)
765
+ row_item.append(store_info)
766
+ row_item.append(self.config.shein_store_manager.get(str(store_username).lower()))
767
+ row_item.append(returnReasonTypeName)
768
+ row_item.append(returnReason)
769
+ row_item.append(skc_img)
770
+ row_item.append(skc_info)
771
+ row_item.append(supplierSku)
772
+ row_item.append(suffixZh)
773
+ row_item.append(returnQuantity)
774
+ row_item.append(platformSku)
775
+ row_item.append(self.bridge.get_sku_supplier(supplierSku, erp))
776
+ row_item.append(self.bridge.get_sku_cost(supplierSku, erp))
777
+ row_item.append(package_name)
778
+ row_item.append(package_no)
779
+ row_item.append(returnPlanNo)
780
+ row_item.append(sellerOrderNo)
781
+ row_item.append(sellerDeliveryNo)
782
+ row_item.append(returnWayTypeName)
783
+ row_item.append(returnExpressCompanyName)
784
+ row_item.append(expressNoList)
785
+ row_item.append(returnAddress)
786
+ row_item.append(sellerContract)
787
+ row_item.append(sellerContractPhone)
788
+ row_item.append(urls)
789
+
790
+ excel_data.append(row_item)
197
791
 
198
792
  sheet_name = '昨日退货列表'
199
793
 
@@ -202,18 +796,20 @@ class SheinExcel:
202
796
 
203
797
  batch_excel_operations(self.config.excel_return_list, [
204
798
  (sheet_name, 'write', excel_data, ['W', 'Z']),
205
- (sheet_name, 'format', self.format_return_list)
799
+ (sheet_name, 'format', self.format_return_list),
800
+ ('Sheet1', 'delete')
206
801
  ])
207
802
 
208
803
  def format_return_list(self, sheet):
209
- merge_by_column_v2(sheet, '退货单号', ['签收状态', '店铺信息', '店长', '退货类型', '退货原因', 'SKC图片', 'SKC信息', '包裹名', '包裹号', '退货计划单号', '订单号', '发货单', '退货出库时间', '退回方式', '快递名称', '运单号', '退货地址', '商家联系人', '商家手机号', '入库问题图片地址'])
804
+ merge_by_column_v2(sheet, '包裹号', ['包裹名'])
805
+ merge_by_column_v2(sheet, '退货单号', ['签收状态', '店铺信息', '店长', '退货类型', '退货原因', 'SKC图片', 'SKC信息', '退货计划单号', '订单号', '发货单', '退货出库时间', '退回方式', '快递名称', '运单号', '退货地址', '商家联系人', '商家手机号', '入库问题图片地址'])
210
806
  beautify_title(sheet)
211
807
  add_borders(sheet)
212
808
  format_to_datetime(sheet, ['时间'])
213
809
  format_to_money(sheet, ['单价', '金额', '成本'])
214
810
  column_to_right(sheet, ['单价', '金额', '成本'])
215
811
  wrap_column(sheet, ['退货原因', '退货地址', '入库问题图片地址'])
216
- autofit_column(sheet, ['店铺别名', 'SKC信息'])
812
+ autofit_column(sheet, ['店铺信息', '店铺别名', 'SKC信息'])
217
813
  column_to_left(sheet, ['店铺信息', '商家SKU', '供方货号', '属性集', 'SKC信息', '退货地址'])
218
814
  specify_column_width(sheet, ['退货原因', 'SKC信息', '商家SKU', '退货地址'], 200 / 6)
219
815
  InsertImageV2(sheet, ['SKC图片'])
@@ -927,9 +1523,9 @@ class SheinExcel:
927
1523
  excel_path_month = str(self.config.excel_shein_finance_month_report).replace('#store_name#', store_name)
928
1524
 
929
1525
  month_data = [[
930
- '平台SKU', '商家SKU', '属性集', '数量', '单价', '金额', '单价成本', '利润', '售出数量', '售出金额', '添加时间',
1526
+ '平台SKU', '商家SKU', '属性集', '数量', '单价', '金额', '单价成本', '利润', '售出数量', '售出金额', '售出成本', '添加时间',
931
1527
  '业务单号', '单据号', '变动类型', '结算类型', 'SKC', '供方货号', '供应商名称', 'SKU图片',
932
- ], ['合计', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']]
1528
+ ], ['合计', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']]
933
1529
  log('len(ledger_list)', len(ledger_list))
934
1530
  for month_item in ledger_list:
935
1531
  row_item = []
@@ -948,6 +1544,7 @@ class SheinExcel:
948
1544
  month_item['quantity'] if month_item['cost_price'] and month_item['settleTypeName'] == '收入结算' else 0)
949
1545
  row_item.append(
950
1546
  month_item['amount'] if month_item['cost_price'] and month_item['settleTypeName'] == '收入结算' else 0)
1547
+ row_item.append('')
951
1548
  row_item.append(month_item['addTime'])
952
1549
  row_item.append(month_item['businessNo'])
953
1550
  row_item.append(month_item['billNo'])
@@ -968,10 +1565,11 @@ class SheinExcel:
968
1565
  add_borders(sheet)
969
1566
  format_to_money(sheet, ['单价', '金额', '利润'])
970
1567
  format_to_datetime(sheet, ['时间'])
971
- add_formula_for_column(sheet, '利润', '=IF(AND(ISNUMBER(G3),O3="收入结算"),F3-G3*D3,0)', 3)
972
- add_formula_for_column(sheet, '售出数量', '=IF(AND(ISNUMBER(G3),O3="收入结算"),D3,0)', 3)
973
- add_formula_for_column(sheet, '售出金额', '=IF(AND(ISNUMBER(G3),O3="收入结算"),F3,0)', 3)
974
- add_sum_for_cell(sheet, ['数量', '金额', '利润', '售出数量', '售出金额'])
1568
+ add_formula_for_column(sheet, '利润', '=IF(AND(ISNUMBER(G3),P3="收入结算"),F3-G3*D3,0)', 3)
1569
+ add_formula_for_column(sheet, '售出数量', '=IF(AND(ISNUMBER(G3),P3="收入结算"),D3,0)', 3)
1570
+ add_formula_for_column(sheet, '售出金额', '=IF(AND(ISNUMBER(G3),P3="收入结算"),F3,0)', 3)
1571
+ add_formula_for_column(sheet, '售出成本', '=IF(AND(ISNUMBER(G3),P3="收入结算"),D3 * G3,0)', 3)
1572
+ add_sum_for_cell(sheet, ['数量', '金额', '利润', '售出数量', '售出金额', '售出成本'])
975
1573
  column_to_left(sheet, ['平台SKU', '商家SKU', '属性集'])
976
1574
  column_to_right(sheet, ['单价', '金额', '利润'])
977
1575
  hidden_columns(sheet, ['SKU图片'])
@@ -1088,7 +1686,7 @@ class SheinExcel:
1088
1686
 
1089
1687
  write_dict_to_file_ex(f'{self.config.auto_dir}/shein/cache/sheet_{last_month}_补扣款列表.json', {store_username: replenish_data[:1] + replenish_data[2:]}, [store_username])
1090
1688
 
1091
- write_data(excel_path_month, sheet_name, replenish_data)
1689
+ write_data(excel_path_month, sheet_name, sort_by_column(replenish_data, 2, 2))
1092
1690
 
1093
1691
  app, wb, sheet = open_excel(excel_path_month, sheet_name)
1094
1692
  set_title_style(sheet, 2)
@@ -1258,23 +1856,33 @@ class SheinExcel:
1258
1856
 
1259
1857
  target_month = find_column_by_data(sheet, 2, last_month)
1260
1858
  sheet.range(f'{target_month}3').value = f"='{last_month}月销售明细'!I2"
1859
+
1261
1860
  sheet.range(f'{target_month}4').number_format = f"¥#,##0.00;¥-#,##0.00"
1262
1861
  sheet.range(f'{target_month}4').value = f"='{last_month}月销售明细'!J2"
1862
+
1863
+ sheet.range(f'A5').value = f"销售成本"
1263
1864
  sheet.range(f'{target_month}5').number_format = f"¥#,##0.00;¥-#,##0.00"
1264
- sheet.range(f'{target_month}5').value = f"='{last_month}月销售明细'!H2"
1865
+ sheet.range(f'{target_month}5').value = f"='{last_month}月销售明细'!K2"
1866
+
1867
+ sheet.range(f'A6').value = f"销售利润"
1265
1868
  sheet.range(f'{target_month}6').number_format = f"¥#,##0.00;¥-#,##0.00"
1869
+ sheet.range(f'{target_month}6').value = f"='{last_month}月销售明细'!H2"
1870
+
1871
+ # sheet.range(f'{target_month}6').number_format = f"¥#,##0.00;¥-#,##0.00"
1266
1872
  # sheet.range(f'{target_month}6').value = f"=-'{last_month}月退货与报废单列表'!L2 * 3"
1267
1873
  sheet.range(f'{target_month}7').number_format = f"¥#,##0.00;¥-#,##0.00"
1268
1874
  sheet.range(f'{target_month}7').value = f"=-'{last_month}月补扣款列表'!H2"
1269
1875
  sheet.range(f'{target_month}8').number_format = f"¥#,##0.00;¥-#,##0.00"
1270
1876
  sheet.range(f'{target_month}9').number_format = f"¥#,##0.00;¥-#,##0.00"
1271
- sheet.range(f'{target_month}9').value = f"=SUM({target_month}5:{target_month}8)"
1877
+ sheet.range(f'{target_month}9').value = f"=SUM({target_month}6:{target_month}8)"
1272
1878
  sheet.range(f'{target_month}10').number_format = f"¥#,##0.00;¥-#,##0.00"
1273
- sheet.range(f'{target_month}10').value = f"='{last_month}月库存结余'!J2"
1879
+ sheet.range(f'{target_month}10').value = f"='{last_month}月库存结余'!Q2"
1880
+
1274
1881
  sheet.range('A1').value = f'2025年{last_month}月 shein 利润汇总表 {store_name}'
1275
1882
  sheet.range(f'{target_month}:{target_month}').autofit()
1276
1883
  wb.save()
1277
1884
  close_excel(app, wb)
1885
+ WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path_month)
1278
1886
 
1279
1887
  def write_summary_algorithm_1(self):
1280
1888
  excel_path = self.config.excel_shein_finance_month_report_summary
@@ -1350,6 +1958,7 @@ class SheinExcel:
1350
1958
  continue
1351
1959
  row[2] = dict_store_manager_shein.get(str(mall_name).lower())
1352
1960
  self.write_to_one(filtered_value, excel_path, sheet_name)
1961
+ WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
1353
1962
 
1354
1963
  def sumary_part(self):
1355
1964
  excel_path = self.config.excel_shein_finance_month_report_summary
@@ -1480,7 +2089,7 @@ class SheinExcel:
1480
2089
  store_excel_data = [header]
1481
2090
  for comment in comment_list:
1482
2091
  row_item = []
1483
- row_item.append(comment['commentId'])
2092
+ row_item.append(f'{comment['commentId']}\n{store_name}')
1484
2093
  row_item.append(comment['goodsThumb'])
1485
2094
  product_info = f'属性:{comment["goodsAttribute"]}\n货号:{comment["goodSn"]}\nSPU:{comment["spu"]}\nSKC:{comment["skc"]}\nSKU:{comment["sku"]}'
1486
2095
  row_item.append(product_info)
@@ -2158,7 +2767,7 @@ class SheinExcel:
2158
2767
  self.dealFormula(sheet) # 有空再封装优化
2159
2768
  colorize_by_field(sheet, 'SPU')
2160
2769
  autofit_column(sheet, ['商品信息', '店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
2161
- column_to_left(sheet, ['店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
2770
+ column_to_left(sheet, ['店铺名称', 'SKC点击率/SKC转化率', '自主参与活动', '近7天SKU销量/SKC销量/SKC曝光'])
2162
2771
  specify_column_width(sheet, ['商品标题'], 150 / 6)
2163
2772
  add_borders(sheet)
2164
2773
  InsertImageV2(sheet, ['SKC图片', 'SKU图片'], 'shein', 120, None, None, True)
@@ -2246,3 +2855,533 @@ class SheinExcel:
2246
2855
  sheet.range(rangeF).number_format = '0.00'
2247
2856
  sheet.range(rangeG).formula = f'=IF(ISNUMBER({rangeB}),{col_week_1}{row}*{col_gross_profit}{row},"")'
2248
2857
  sheet.range(rangeG).number_format = '0.00'
2858
+
2859
+ def write_check_order(self, erp, start_date, end_date):
2860
+ header = ['店铺账号', '店铺别名', '店长', '报账单号', '货号', 'SKC', '平台SKU', '商家SKU', '属性集', '商品数量', '账单类型', '收支类型', '状态', '币种', '金额', 'ERP成本',
2861
+ '成本总额', '业务单号', '费用类型', '备注', '来源单号', '账单创建时间', '台账添加时间', '报账时间', '预计结算日期', '实际结算日期']
2862
+ excel_data = [header]
2863
+
2864
+ dict_store = read_dict_from_file(self.config.shein_store_alias)
2865
+
2866
+ cache_file = f'{self.config.auto_dir}/shein/cache/check_order_{start_date}_{end_date}.json'
2867
+ dict = read_dict_from_file(cache_file)
2868
+ for store_username, data_list in dict.items():
2869
+ for item in data_list:
2870
+ store_name = dict_store.get(store_username)
2871
+ store_manager = self.config.shein_store_manager.get(str(store_username).lower())
2872
+
2873
+ row_item = []
2874
+ row_item.append(store_username)
2875
+ row_item.append(store_name)
2876
+ row_item.append(store_manager)
2877
+ row_item.append(item['reportOrderNo'])
2878
+ row_item.append(item['goodsSn'])
2879
+ row_item.append(item['skcName'])
2880
+ row_item.append(item['skuCode'])
2881
+ row_item.append(item['skuSn'])
2882
+ row_item.append(item['suffix'])
2883
+ row_item.append(item['goodsCount'])
2884
+ row_item.append(item['secondOrderTypeName'])
2885
+ row_item.append(item['inAndOutName'])
2886
+ row_item.append(item['settlementStatusName'])
2887
+ row_item.append(item['settleCurrencyCode'])
2888
+ row_item.append(item['income'])
2889
+ row_item.append(self.bridge.get_sku_cost(item['skuSn'], erp))
2890
+ row_item.append('')
2891
+ row_item.append(item['bzOrderNo'])
2892
+ row_item.append(item['expenseTypeName'])
2893
+ row_item.append(item['remark'])
2894
+ row_item.append(item['sourceNo'])
2895
+ row_item.append(item['addTime'])
2896
+ row_item.append(item['businessCompletedTime'])
2897
+ row_item.append(item['reportTime'])
2898
+ row_item.append(item['estimatePayTime'])
2899
+ row_item.append(item['completedPayTime'])
2900
+
2901
+ excel_data.append(row_item)
2902
+
2903
+ cache_file_excel = f'{self.config.auto_dir}/shein/cache/shein_return_order_list_excel_{start_date}_{end_date}.json'
2904
+ write_dict_to_file(cache_file_excel, excel_data)
2905
+
2906
+ sheet_name = '收支明细'
2907
+ batch_excel_operations(self.config.excel_shein_finance_month_report_pop, [
2908
+ (sheet_name, 'write', excel_data, ['R']),
2909
+ (sheet_name, 'format', self.format_check_order)
2910
+ ])
2911
+
2912
+ header = ['店铺账号', '店铺别名', '店长', '出库金额', '出库成本', '备货作业费', '代收服务费', '订单履约服务费', '订单退货', '退货处理费', '退货单履约服务费', '利润']
2913
+ excel_data = [header]
2914
+ cache_file = f'{self.config.auto_dir}/shein/cache/check_order_{start_date}_{end_date}.json'
2915
+ dict = read_dict_from_file(cache_file)
2916
+ for store_username, data_list in dict.items():
2917
+ store_name = dict_store.get(store_username)
2918
+ store_manager = self.config.shein_store_manager.get(str(store_username).lower())
2919
+ row_item = []
2920
+ row_item.append(store_username)
2921
+ row_item.append(store_name)
2922
+ row_item.append(store_manager)
2923
+ row_item.append('')
2924
+ row_item.append('')
2925
+ row_item.append('')
2926
+ row_item.append('')
2927
+ row_item.append('')
2928
+ row_item.append('')
2929
+ row_item.append('')
2930
+ row_item.append('')
2931
+ row_item.append('')
2932
+ excel_data.append(row_item)
2933
+
2934
+ sheet_name = '总表'
2935
+ batch_excel_operations(self.config.excel_shein_finance_month_report_pop, [
2936
+ (sheet_name, 'write', excel_data),
2937
+ (sheet_name, 'format', self.format_check_order),
2938
+ ('Sheet1', 'delete'),
2939
+ (sheet_name, 'move', 1),
2940
+ ])
2941
+
2942
+ def merge_finance_details_to_summary(self, source_dir, start_date, end_date, output_dir=None):
2943
+ """
2944
+ 将多个店铺的财务收支明细Excel文件合并到一个汇总Excel中
2945
+
2946
+ Args:
2947
+ source_dir: 源文件目录路径
2948
+ start_date: 开始日期,格式: YYYY-MM-DD
2949
+ end_date: 结束日期,格式: YYYY-MM-DD
2950
+ output_dir: 输出目录,默认为None则使用source_dir
2951
+
2952
+ Returns:
2953
+ 汇总Excel文件路径
2954
+ """
2955
+ import os
2956
+ import glob
2957
+
2958
+ # 确定输出目录
2959
+ if output_dir is None:
2960
+ output_dir = source_dir
2961
+ os.makedirs(output_dir, exist_ok=True)
2962
+
2963
+ # 提取月份(从start_date中提取)
2964
+ from datetime import datetime
2965
+ start_dt = datetime.strptime(start_date, '%Y-%m-%d')
2966
+ month_str = f"{start_dt.month}月"
2967
+
2968
+ # 生成输出文件名
2969
+ output_filename = f'希音财务月报POP-{month_str}.xlsx'
2970
+ output_path = os.path.join(output_dir, output_filename)
2971
+
2972
+ log(f'开始合并财务收支明细: {source_dir}')
2973
+
2974
+ # 查找所有匹配的Excel文件
2975
+ pattern = os.path.join(source_dir, f'finance_details_*_{start_date}_{end_date}.xlsx')
2976
+ excel_files = glob.glob(pattern)
2977
+
2978
+ if len(excel_files) == 0:
2979
+ log(f'未找到匹配的文件: {pattern}')
2980
+ raise Exception(f'未找到匹配的财务收支明细文件')
2981
+
2982
+ log(f'找到 {len(excel_files)} 个Excel文件待合并')
2983
+
2984
+ # 读取所有Excel文件并合并数据
2985
+ all_detail_data = []
2986
+ store_list = [] # 存储店铺账号列表
2987
+ header = None
2988
+
2989
+ for idx, excel_file in enumerate(excel_files):
2990
+ log(f'读取文件 {idx + 1}/{len(excel_files)}: {os.path.basename(excel_file)}')
2991
+
2992
+ # 从文件名中提取店铺账号
2993
+ filename = os.path.basename(excel_file)
2994
+ # 格式: finance_details_{store_username}_{start_date}_{end_date}.xlsx
2995
+ parts = filename.replace('.xlsx', '').split('_')
2996
+ if len(parts) >= 5:
2997
+ store_username = parts[2] # finance_details_{store_username}_...
2998
+ store_list.append(store_username)
2999
+ else:
3000
+ log(f'警告:无法从文件名提取店铺账号: {filename}')
3001
+ store_username = 'unknown'
3002
+ store_list.append(store_username)
3003
+
3004
+ # 读取Excel文件
3005
+ try:
3006
+ df = pd.read_excel(excel_file, sheet_name=0)
3007
+
3008
+ if idx == 0:
3009
+ # 第一个文件,保存表头
3010
+ header = df.columns.tolist()
3011
+ log(f'表头列数: {len(header)}')
3012
+
3013
+ # 读取数据(跳过表头)
3014
+ data_rows = df.values.tolist()
3015
+ log(f'读取到 {len(data_rows)} 行数据')
3016
+
3017
+ # 添加到总数据中
3018
+ all_detail_data.extend(data_rows)
3019
+
3020
+ except Exception as e:
3021
+ log(f'读取文件失败: {excel_file}, 错误: {str(e)}')
3022
+ continue
3023
+
3024
+ log(f'合并完成,总共 {len(all_detail_data)} 行数据')
3025
+
3026
+ dict_store_manager_shein = self.config.shein_store_manager
3027
+ dict_store_name = read_dict_from_file(self.config.shein_store_alias)
3028
+
3029
+ # 在"金额"列后面添加"ERP成本"和"成本总额"两列
3030
+ if header:
3031
+ # 查找"金额"列和"商家SKU"列的位置
3032
+ amount_col_idx = None
3033
+ sku_col_idx = None
3034
+ business_no_col_idx = None # 业务单号列索引
3035
+
3036
+ for i, col_name in enumerate(header):
3037
+ if '金额' in str(col_name):
3038
+ amount_col_idx = i
3039
+ if '商家SKU' in str(col_name) or 'SKU' in str(col_name):
3040
+ sku_col_idx = i
3041
+ if '店铺名称' in str(col_name):
3042
+ store_name_idx = i
3043
+ if '店长' in str(col_name):
3044
+ store_manager_idx = i
3045
+ if '业务单号' in str(col_name):
3046
+ business_no_col_idx = i
3047
+
3048
+ if amount_col_idx is not None:
3049
+ # 在"金额"列后面插入两列
3050
+ new_header = header[:amount_col_idx + 1] + ['ERP成本', '成本总额'] + header[amount_col_idx + 1:]
3051
+ log(f'在第{amount_col_idx + 1}列(金额)后面插入"ERP成本"和"成本总额"两列')
3052
+
3053
+ # 业务单号列在插入新列后的索引需要调整
3054
+ if business_no_col_idx is not None and business_no_col_idx > amount_col_idx:
3055
+ business_no_col_idx_adjusted = business_no_col_idx + 2 # 因为插入了2列
3056
+ else:
3057
+ business_no_col_idx_adjusted = business_no_col_idx
3058
+
3059
+ # 处理数据行:在相应位置插入ERP成本和成本总额
3060
+ new_data = []
3061
+ for row_idx, row in enumerate(all_detail_data):
3062
+ # 转换为list(如果是tuple)
3063
+ row_list = list(row)
3064
+
3065
+ store_username = row_list[0]
3066
+ store_name = dict_store_name.get(store_username)
3067
+ store_manager = dict_store_manager_shein.get(str(store_username).lower(), '-')
3068
+ row_list[1] = store_name
3069
+ row_list[2] = store_manager
3070
+
3071
+ # 获取商家SKU
3072
+ sku = None
3073
+ if sku_col_idx is not None and sku_col_idx < len(row_list):
3074
+ sku = row_list[sku_col_idx]
3075
+
3076
+ # 获取ERP成本
3077
+ erp_cost = ''
3078
+ if sku and self.bridge:
3079
+ try:
3080
+ erp_cost = self.bridge.get_sku_cost(str(sku), self.config.erp_source)
3081
+ except Exception as e:
3082
+ log(f'获取SKU成本失败: {sku}, 错误: {str(e)}')
3083
+
3084
+ # 在"金额"列后面插入两列数据
3085
+ new_row = row_list[:amount_col_idx + 1] + [erp_cost, ''] + row_list[amount_col_idx + 1:]
3086
+ new_data.append(new_row)
3087
+
3088
+ # 每1000行输出一次进度
3089
+ if (row_idx + 1) % 1000 == 0:
3090
+ log(f'处理进度: {row_idx + 1}/{len(all_detail_data)}')
3091
+
3092
+ # 更新表头和数据
3093
+ header = new_header
3094
+ all_detail_data = new_data
3095
+ log(f'ERP成本数据处理完成,新表头列数: {len(header)}')
3096
+ if business_no_col_idx_adjusted is not None:
3097
+ log(f'业务单号列已转换为字符串格式,列索引: {business_no_col_idx_adjusted}')
3098
+ else:
3099
+ log('警告:未找到"金额"列,无法添加ERP成本和成本总额列')
3100
+
3101
+ # 准备汇总表数据
3102
+ summary_header = ['店铺账号', '店铺别名', '店长', '出库金额', '出库成本', '备货作业费',
3103
+ '代收服务费', '订单履约服务费', '订单退货', '退货处理费', '退货单履约服务费', '利润']
3104
+ summary_data = [summary_header]
3105
+
3106
+ # 为每个店铺创建一行(其他字段留空,由公式计算)
3107
+ for store_username in store_list:
3108
+ store_name = dict_store_name.get(store_username)
3109
+ store_manager = dict_store_manager_shein.get(str(store_username).lower(), '-')
3110
+ row = [store_username, store_name, store_manager, '', '', '', '', '', '', '', '', '']
3111
+ summary_data.append(row)
3112
+
3113
+ # 写入Excel文件
3114
+ log(f'开始写入汇总Excel: {output_path}')
3115
+
3116
+ # 查找需要格式化为文本的列
3117
+ text_format_columns = []
3118
+ str_keywords = ['业务单号']
3119
+
3120
+ def col_idx_to_letter(idx):
3121
+ """将列索引转换为Excel列字母 (0->A, 1->B, ..., 25->Z, 26->AA, ...)"""
3122
+ result = ''
3123
+ idx += 1 # Excel列从1开始
3124
+ while idx > 0:
3125
+ idx -= 1
3126
+ result = chr(65 + idx % 26) + result
3127
+ idx //= 26
3128
+ return result
3129
+
3130
+ for col_idx, col_name in enumerate(header):
3131
+ col_name_str = str(col_name)
3132
+ # 检查列名是否包含需要保持为文本的关键词
3133
+ if any(keyword in col_name_str for keyword in str_keywords):
3134
+ col_letter = col_idx_to_letter(col_idx)
3135
+ text_format_columns.append(col_letter)
3136
+ log(f'列"{col_name}"(第{col_idx}列,Excel列{col_letter})将格式化为文本')
3137
+
3138
+ log(f'共{len(text_format_columns)}列需要格式化为文本: {text_format_columns}')
3139
+
3140
+ # 使用batch_excel_operations批量写入和格式化
3141
+ operations = [
3142
+ ('财务收支明细', 'write', [header] + all_detail_data, text_format_columns),
3143
+ ('财务收支明细', 'format', self.format_check_order),
3144
+ ('总表', 'write', summary_data),
3145
+ ('总表', 'format', self.format_check_order),
3146
+ ('Sheet1', 'delete'),
3147
+ ('总表', 'move', 1),
3148
+ ]
3149
+
3150
+ batch_excel_operations(output_path, operations)
3151
+
3152
+ log(f'合并完成,文件已保存: {output_path}')
3153
+ return output_path
3154
+
3155
+ def format_check_order(self, sheet):
3156
+ if sheet.name == '收支明细' or sheet.name == '财务收支明细':
3157
+ beautify_title(sheet)
3158
+ add_borders(sheet)
3159
+ format_to_datetime(sheet, ['时间'])
3160
+ format_to_date(sheet, ['日期'])
3161
+ format_to_money(sheet, ['金额', '成本', 'ERP成本', '成本总额'])
3162
+ column_to_right(sheet, ['金额', '成本', 'ERP成本', '成本总额'])
3163
+ column_to_left(sheet, ['货号', '商家SKU'])
3164
+
3165
+ # 将业务单号列格式化为文本格式
3166
+ try:
3167
+ from .fun_excel import find_column_by_data
3168
+ business_no_col = find_column_by_data(sheet, 1, '业务单号')
3169
+ if business_no_col:
3170
+ # 设置整列为文本格式
3171
+ last_row = sheet.range('A' + str(sheet.cells.last_cell.row)).end('up').row
3172
+ sheet.range(f'{business_no_col}2:{business_no_col}{last_row}').number_format = '@'
3173
+ log(f'业务单号列({business_no_col})已设置为文本格式')
3174
+ except Exception as e:
3175
+ log(f'设置业务单号列格式失败: {str(e)}')
3176
+
3177
+ # 为成本总额列添加公式
3178
+ # 成本总额 = 数量 * ERP成本
3179
+ try:
3180
+ # 查找"数量"列和"ERP成本"列的位置
3181
+ from .fun_excel import find_column_by_data
3182
+ quantity_col = find_column_by_data(sheet, 1, '商品数量')
3183
+ erp_cost_col = find_column_by_data(sheet, 1, 'ERP成本')
3184
+
3185
+ log("数量,ERP成本", quantity_col, erp_cost_col)
3186
+ if quantity_col and erp_cost_col:
3187
+ # 添加公式:成本总额 = 数量 * ERP成本
3188
+ add_formula_for_column(sheet, '成本总额', f'=IF(ISNUMBER({erp_cost_col}2),{quantity_col}2*{erp_cost_col}2,"-")')
3189
+ log('成本总额公式已添加')
3190
+ except Exception as e:
3191
+ log(f'添加成本总额公式失败: {str(e)}')
3192
+
3193
+ if sheet.name == '总表':
3194
+ beautify_title(sheet)
3195
+ add_borders(sheet)
3196
+ format_to_money(sheet, ['金额', '成本', '费', '订单退货', '利润'])
3197
+ column_to_right(sheet, ['金额', '成本', '费', '订单退货', '利润'])
3198
+
3199
+ # 使用财务收支明细sheet的引用
3200
+ detail_sheet = '财务收支明细' if '财务收支明细' in [s.name for s in sheet.book.sheets] else '收支明细'
3201
+
3202
+ # 查找财务收支明细sheet中各列的位置
3203
+ from .fun_excel import find_column_by_data
3204
+ detail_ws = None
3205
+ for ws in sheet.book.sheets:
3206
+ if ws.name == detail_sheet:
3207
+ detail_ws = ws
3208
+ break
3209
+
3210
+ if detail_ws:
3211
+ # 查找关键列的位置
3212
+ amount_col = find_column_by_data(detail_ws, 1, '金额') # 金额列
3213
+ cost_total_col = find_column_by_data(detail_ws, 1, '成本总额') # 成本总额列
3214
+ store_col = find_column_by_data(detail_ws, 1, '店铺账号') # 店铺账号列
3215
+ type_col = find_column_by_data(detail_ws, 1, '收支类型') # 收支类型列
3216
+ bill_type_col = find_column_by_data(detail_ws, 1, '账单类型') # 账单类型列
3217
+
3218
+ log(f'找到列位置: 金额={amount_col}, 成本总额={cost_total_col}, 店铺账号={store_col}, 收支类型={type_col}, 账单类型={bill_type_col}')
3219
+
3220
+ # 使用找到的列位置生成公式
3221
+ if amount_col and store_col and type_col:
3222
+ add_formula_for_column(sheet, '出库金额', f'=SUMIFS({detail_sheet}!{amount_col}:{amount_col},{detail_sheet}!{store_col}:{store_col},A2,{detail_sheet}!{type_col}:{type_col},"收入")')
3223
+
3224
+ if cost_total_col and store_col and type_col:
3225
+ # 使用成本总额列
3226
+ add_formula_for_column(sheet, '出库成本', f'=SUMIFS({detail_sheet}!{cost_total_col}:{cost_total_col},{detail_sheet}!{store_col}:{store_col},A2,{detail_sheet}!{type_col}:{type_col},"收入")')
3227
+
3228
+ if amount_col and store_col and type_col and bill_type_col:
3229
+ add_formula_for_column(sheet, '备货作业费', f'=SUMIFS({detail_sheet}!{amount_col}:{amount_col},{detail_sheet}!{store_col}:{store_col},A2,{detail_sheet}!{type_col}:{type_col},"支出",{detail_sheet}!{bill_type_col}:{bill_type_col},"备货作业费")')
3230
+ add_formula_for_column(sheet, '代收服务费', f'=SUMIFS({detail_sheet}!{amount_col}:{amount_col},{detail_sheet}!{store_col}:{store_col},A2,{detail_sheet}!{type_col}:{type_col},"支出",{detail_sheet}!{bill_type_col}:{bill_type_col},"代收服务费")')
3231
+ add_formula_for_column(sheet, '订单履约服务费', f'=SUMIFS({detail_sheet}!{amount_col}:{amount_col},{detail_sheet}!{store_col}:{store_col},A2,{detail_sheet}!{type_col}:{type_col},"支出",{detail_sheet}!{bill_type_col}:{bill_type_col},"订单履约服务费")')
3232
+ add_formula_for_column(sheet, '订单退货', f'=SUMIFS({detail_sheet}!{amount_col}:{amount_col},{detail_sheet}!{store_col}:{store_col},A2,{detail_sheet}!{type_col}:{type_col},"支出",{detail_sheet}!{bill_type_col}:{bill_type_col},"订单退货")')
3233
+ add_formula_for_column(sheet, '退货处理费', f'=SUMIFS({detail_sheet}!{amount_col}:{amount_col},{detail_sheet}!{store_col}:{store_col},A2,{detail_sheet}!{type_col}:{type_col},"支出",{detail_sheet}!{bill_type_col}:{bill_type_col},"退货处理费")')
3234
+ add_formula_for_column(sheet, '退货单履约服务费', f'=SUMIFS({detail_sheet}!{amount_col}:{amount_col},{detail_sheet}!{store_col}:{store_col},A2,{detail_sheet}!{type_col}:{type_col},"支出",{detail_sheet}!{bill_type_col}:{bill_type_col},"退货单履约服务费")')
3235
+
3236
+ add_formula_for_column(sheet, '利润', '=D2-E2-F2-G2-H2-I2-J2-K2')
3237
+
3238
+ def write_vssv_order_list(self):
3239
+ """
3240
+ 写入VSSV增值服务订单列表到Excel
3241
+ """
3242
+ # 获取上个月的时间范围
3243
+ first_day, last_day = TimeUtils.get_last_month_range()
3244
+ last_month = TimeUtils.get_last_month()
3245
+
3246
+ # 读取店铺别名映射
3247
+ dict_store = read_dict_from_file(self.config.shein_store_alias)
3248
+
3249
+ # 准备Excel数据
3250
+ header = ['店铺账号', '店铺名称', '增值服务订单号', '增值服务单号', '采购订单号',
3251
+ '平台SKC', '商家SKC', 'SKC数量', '扣款单号', '订单状态', '实际总金额',
3252
+ '增值服务项', '创建时间', '完成时间']
3253
+ excel_data = [header]
3254
+
3255
+ # 遍历vssv_order目录下的所有店铺数据
3256
+ src_directory = f'{self.config.auto_dir}/shein/vssv_order'
3257
+
3258
+ if not os.path.exists(src_directory):
3259
+ log(f'VSSV订单目录不存在: {src_directory}')
3260
+ return
3261
+
3262
+ for entry in os.listdir(src_directory):
3263
+ # 检查是否为匹配的缓存文件
3264
+ if entry.startswith(f"vssv_order_list_") and entry.endswith(f"_{first_day}_{last_day}.json"):
3265
+ file_path = os.path.join(src_directory, entry)
3266
+
3267
+ # 从文件名中提取店铺账号
3268
+ # 格式: vssv_order_list_{store_username}_{first_day}_{last_day}.json
3269
+ parts = entry.replace('.json', '').split('_')
3270
+ if len(parts) >= 5:
3271
+ # vssv_order_list_{store_username}_{first_day}_{last_day}
3272
+ # parts[0]='vssv', parts[1]='order', parts[2]='list', parts[3]=store_username
3273
+ store_username = parts[3]
3274
+ else:
3275
+ log(f'无法解析店铺账号: {entry}')
3276
+ continue
3277
+
3278
+ # 获取店铺名称
3279
+ store_name = dict_store.get(store_username, store_username)
3280
+
3281
+ # 读取订单数据
3282
+ order_list = read_dict_from_file(file_path)
3283
+ log(f'读取店铺 {store_name}({store_username}) 的VSSV订单: {len(order_list)}条')
3284
+
3285
+ # 处理每条订单数据
3286
+ for order in order_list:
3287
+ # 基础订单信息
3288
+ order_no = order.get('orderNo', '-')
3289
+ sub_order_no = order.get('subOrderNo', '-')
3290
+ purchase_no = order.get('purchaseNo', '-')
3291
+ skc_img_path = order.get('skcImgPath', '')
3292
+ skc = order.get('skc', '-')
3293
+ supplier_product_number = order.get('supplierProductNumber', '-')
3294
+ skc_num = order.get('skcNum', 0)
3295
+ order_state_name = order.get('orderStateName', '-')
3296
+ actual_total_amount = order.get('actualTotalAmount', 0)
3297
+
3298
+ # 提取扣款单号(从vendorRepairList数组中)
3299
+ vendor_repair_no = '-'
3300
+ vendor_repair_list = order.get('vendorRepairList', [])
3301
+ if vendor_repair_list and len(vendor_repair_list) > 0:
3302
+ vendor_repair_no = vendor_repair_list[0].get('vendorRepairNo', '-')
3303
+
3304
+ # 提取创建时间和完成时间(从orderChangeLogVo中)
3305
+ create_time = '-'
3306
+ finish_time = '-'
3307
+ order_change_log = order.get('orderChangeLogVo', [])
3308
+ for log_item in order_change_log:
3309
+ if log_item.get('operateType') == 12: # 创建时间
3310
+ create_time = log_item.get('operateTime', '-')
3311
+ elif log_item.get('operateType') == 4: # 增值订单完成时间
3312
+ finish_time = log_item.get('operateTime', '-')
3313
+
3314
+ # 获取增值服务项列表并合并成一个字符串
3315
+ service_items = order.get('subOrderServiceItemVoList', [])
3316
+ service_items_text = '-'
3317
+
3318
+ if service_items:
3319
+ # 将所有服务项合并成一个字符串,每个服务项一行
3320
+ service_lines = []
3321
+ for service_item in service_items:
3322
+ service_name = service_item.get('serviceItemName', '-')
3323
+ settlement_qty = service_item.get('settlementQuantity', 0)
3324
+ item_amount = service_item.get('itemTotalAmount', 0)
3325
+ price = service_item.get('price', 0)
3326
+ # 格式:服务项名称 | 数量 | 金额
3327
+ service_line = f"{service_name}: {settlement_qty}x{price}=¥{item_amount}"
3328
+ service_lines.append(service_line)
3329
+ service_items_text = '\n'.join(service_lines)
3330
+
3331
+ # 添加一行数据
3332
+ row_item = []
3333
+ row_item.append(store_username) # 店铺账号
3334
+ row_item.append(store_name) # 店铺名称
3335
+ row_item.append(order_no) # 增值服务订单号
3336
+ row_item.append(sub_order_no) # 增值服务单号
3337
+ row_item.append(purchase_no) # 采购订单号
3338
+ # row_item.append(skc_img_path) # SKC图片
3339
+ row_item.append(skc) # 平台SKC
3340
+ row_item.append(supplier_product_number) # 商家SKC
3341
+ row_item.append(skc_num) # SKC数量
3342
+ row_item.append(vendor_repair_no) # 扣款单号
3343
+ row_item.append(order_state_name) # 订单状态
3344
+ row_item.append(actual_total_amount) # 实际总金额
3345
+ row_item.append(service_items_text) # 增值服务项(合并)
3346
+ row_item.append(create_time) # 创建时间
3347
+ row_item.append(finish_time) # 完成时间
3348
+ excel_data.append(row_item)
3349
+
3350
+ log(f'共收集到 {len(excel_data) - 1} 条VSSV订单数据')
3351
+
3352
+ # 如果没有数据,只有表头,则不生成Excel
3353
+ if len(excel_data) <= 1:
3354
+ log('没有VSSV订单数据,跳过Excel生成')
3355
+ return
3356
+
3357
+ # 写入Excel
3358
+ excel_path = self.config.excel_path
3359
+ sheet_name = f'{last_month}月增值服务列表'
3360
+
3361
+ batch_excel_operations(excel_path, [
3362
+ (sheet_name, 'write', excel_data, ['C', 'D', 'E', 'I']), # 订单号、单号、采购单号、扣款单号格式化为文本
3363
+ (sheet_name, 'format', self.format_vssv_order_list),
3364
+ ('Sheet1', 'delete')
3365
+ ])
3366
+
3367
+ log(f'VSSV订单列表已写入: {excel_path}')
3368
+
3369
+ def format_vssv_order_list(self, sheet):
3370
+ """
3371
+ 格式化VSSV订单列表Excel
3372
+ """
3373
+ beautify_title(sheet)
3374
+ add_borders(sheet)
3375
+ format_to_money(sheet, ['金额', '总金额'])
3376
+ column_to_right(sheet, ['金额', '数量', '总金额'])
3377
+ format_to_datetime(sheet, ['时间'])
3378
+ column_to_left(sheet, ['店铺账号', '订单号', '单号', 'SKC', '增值服务项'])
3379
+ wrap_column(sheet, ['增值服务项']) # 增值服务项列自动换行
3380
+ autofit_column(sheet, ['店铺名称', '订单状态'])
3381
+ specify_column_width(sheet, ['增值服务订单号', '增值服务单号', '采购订单号', '扣款单号'], 160 / 6)
3382
+ specify_column_width(sheet, ['增值服务项'], 280 / 6) # 服务项列设置较宽
3383
+
3384
+ # 插入SKC图片
3385
+ # InsertImageV2(sheet, ['SKC图片'], 'shein', 90)
3386
+
3387
+ sheet.autofit()