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/RateLimitedSender.py +45 -45
- qrpa/__init__.py +31 -26
- qrpa/db_migrator.py +600 -600
- qrpa/feishu_bot_app.py +267 -267
- qrpa/feishu_client.py +410 -0
- qrpa/feishu_logic.py +1443 -0
- qrpa/fun_base.py +339 -337
- qrpa/fun_excel.py +529 -61
- qrpa/fun_file.py +318 -318
- qrpa/fun_web.py +258 -148
- qrpa/fun_win.py +198 -198
- qrpa/mysql_module/__init__.py +0 -0
- qrpa/mysql_module/new_product_analysis_model.py +556 -0
- qrpa/mysql_module/shein_ledger_model.py +468 -0
- qrpa/mysql_module/shein_product_model.py +495 -0
- qrpa/mysql_module/shein_return_order_model.py +569 -0
- qrpa/mysql_module/shein_store_model.py +595 -0
- qrpa/shein_daily_report_model.py +375 -375
- qrpa/shein_excel.py +1248 -109
- qrpa/shein_lib.py +2333 -143
- qrpa/shein_mysql.py +92 -0
- qrpa/shein_sqlite.py +153 -153
- qrpa/shein_ziniao.py +529 -450
- qrpa/temu_chrome.py +56 -56
- qrpa/temu_excel.py +139 -139
- qrpa/temu_lib.py +156 -154
- qrpa/time_utils.py +87 -50
- qrpa/time_utils_example.py +243 -243
- qrpa/wxwork.py +318 -318
- {qrpa-1.0.34.dist-info → qrpa-1.1.50.dist-info}/METADATA +1 -1
- qrpa-1.1.50.dist-info/RECORD +33 -0
- qrpa-1.0.34.dist-info/RECORD +0 -24
- {qrpa-1.0.34.dist-info → qrpa-1.1.50.dist-info}/WHEEL +0 -0
- {qrpa-1.0.34.dist-info → qrpa-1.1.50.dist-info}/top_level.txt +0 -0
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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, '
|
|
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),
|
|
972
|
-
add_formula_for_column(sheet, '售出数量', '=IF(AND(ISNUMBER(G3),
|
|
973
|
-
add_formula_for_column(sheet, '售出金额', '=IF(AND(ISNUMBER(G3),
|
|
974
|
-
|
|
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}月销售明细'!
|
|
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}
|
|
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}月库存结余'!
|
|
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()
|