qrpa 1.0.96__py3-none-any.whl → 1.0.98__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qrpa might be problematic. Click here for more details.
- qrpa/feishu_client.py +410 -410
- qrpa/feishu_logic.py +1443 -1443
- qrpa/fun_excel.py +1 -0
- qrpa/fun_web.py +65 -7
- qrpa/shein_excel.py +131 -14
- qrpa/shein_lib.py +47 -0
- qrpa/shein_mysql.py +62 -62
- qrpa/shein_ziniao.py +1 -1
- qrpa/time_utils.py +30 -43
- qrpa/wxwork.py +318 -318
- {qrpa-1.0.96.dist-info → qrpa-1.0.98.dist-info}/METADATA +1 -1
- {qrpa-1.0.96.dist-info → qrpa-1.0.98.dist-info}/RECORD +14 -14
- {qrpa-1.0.96.dist-info → qrpa-1.0.98.dist-info}/WHEEL +0 -0
- {qrpa-1.0.96.dist-info → qrpa-1.0.98.dist-info}/top_level.txt +0 -0
qrpa/fun_excel.py
CHANGED
|
@@ -2149,6 +2149,7 @@ def add_formula_for_column(sheet, col_name, formula, start_row=2):
|
|
|
2149
2149
|
# AutoFill 快速填充到所有行(start_row 到 last_row)
|
|
2150
2150
|
sheet.range(f'{col_letter}{start_row}').api.AutoFill(
|
|
2151
2151
|
sheet.range(f'{col_letter}{start_row}:{col_letter}{last_row}').api)
|
|
2152
|
+
sheet.range(f'{col_letter}:{col_letter}').autofit()
|
|
2152
2153
|
|
|
2153
2154
|
def autofit_column(sheet, columns=None):
|
|
2154
2155
|
if columns is None:
|
qrpa/fun_web.py
CHANGED
|
@@ -10,12 +10,13 @@ import inspect
|
|
|
10
10
|
def fetch(page: Page, url: str, params: Optional[Union[dict, list, str]] = None, headers: Optional[dict] = None, config:
|
|
11
11
|
Optional[dict] = None) -> dict:
|
|
12
12
|
"""
|
|
13
|
-
发送 HTTP POST 请求,支持自定义 headers
|
|
13
|
+
发送 HTTP POST 请求,支持自定义 headers 和重定向处理。
|
|
14
14
|
|
|
15
15
|
:param page: Playwright 的 Page 对象
|
|
16
16
|
:param url: 请求地址
|
|
17
17
|
:param params: 请求参数(dict、list、str 或 None)
|
|
18
18
|
:param headers: 自定义 headers 字典
|
|
19
|
+
:param config: 请求配置字典
|
|
19
20
|
:return: 服务器返回的 JSON 响应(dict)
|
|
20
21
|
"""
|
|
21
22
|
if params is not None and not isinstance(params, (dict, list, str)):
|
|
@@ -26,7 +27,7 @@ Optional[dict] = None) -> dict:
|
|
|
26
27
|
try:
|
|
27
28
|
page.wait_for_load_state('load')
|
|
28
29
|
response = page.evaluate("""
|
|
29
|
-
async ({ url, params, extraHeaders }) => {
|
|
30
|
+
async ({ url, params, extraHeaders, config }) => {
|
|
30
31
|
try {
|
|
31
32
|
const defaultHeaders = {
|
|
32
33
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
|
@@ -37,9 +38,15 @@ Optional[dict] = None) -> dict:
|
|
|
37
38
|
const options = {
|
|
38
39
|
method: 'POST',
|
|
39
40
|
credentials: 'include',
|
|
41
|
+
redirect: 'follow', // 明确设置跟随重定向
|
|
40
42
|
headers: headers
|
|
41
43
|
};
|
|
42
44
|
|
|
45
|
+
// 应用额外配置
|
|
46
|
+
if (config) {
|
|
47
|
+
Object.assign(options, config);
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
if (params !== null) {
|
|
44
51
|
if (typeof params === 'string') {
|
|
45
52
|
options.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
|
|
@@ -51,15 +58,40 @@ Optional[dict] = None) -> dict:
|
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
const response = await fetch(url, options);
|
|
61
|
+
|
|
62
|
+
// 处理重定向
|
|
63
|
+
if (response.redirected) {
|
|
64
|
+
console.log(`请求被重定向到: ${response.url}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
if (!response.ok) {
|
|
68
|
+
// 如果是重定向相关的状态码,尝试获取响应内容
|
|
69
|
+
if (response.status >= 300 && response.status < 400) {
|
|
70
|
+
const text = await response.text();
|
|
71
|
+
return {
|
|
72
|
+
"error": "redirect_error",
|
|
73
|
+
"message": `HTTP ${response.status} - ${response.statusText}`,
|
|
74
|
+
"redirect_url": response.url,
|
|
75
|
+
"response_text": text,
|
|
76
|
+
"status": response.status
|
|
77
|
+
};
|
|
78
|
+
}
|
|
55
79
|
throw new Error(`HTTP ${response.status} - ${response.statusText}`);
|
|
56
80
|
}
|
|
57
|
-
|
|
81
|
+
|
|
82
|
+
// 尝试解析 JSON,如果失败则返回文本内容
|
|
83
|
+
const contentType = response.headers.get('content-type');
|
|
84
|
+
if (contentType && contentType.includes('application/json')) {
|
|
85
|
+
return await response.json();
|
|
86
|
+
} else {
|
|
87
|
+
const text = await response.text();
|
|
88
|
+
return { "content": text, "content_type": contentType, "final_url": response.url };
|
|
89
|
+
}
|
|
58
90
|
} catch (error) {
|
|
59
91
|
return { "error": "fetch_failed", "message": error.message };
|
|
60
92
|
}
|
|
61
93
|
}
|
|
62
|
-
""", {"url": url, "params": params, "extraHeaders": headers})
|
|
94
|
+
""", {"url": url, "params": params, "extraHeaders": headers, "config": config})
|
|
63
95
|
|
|
64
96
|
return response
|
|
65
97
|
except Exception as e:
|
|
@@ -148,7 +180,7 @@ def full_screen_shot(web_page: Page, config):
|
|
|
148
180
|
|
|
149
181
|
def fetch_get(page: Page, url: str, headers: Optional[dict] = None, config: Optional[dict] = None) -> dict:
|
|
150
182
|
"""
|
|
151
|
-
发送 HTTP GET 请求,支持自定义 headers
|
|
183
|
+
发送 HTTP GET 请求,支持自定义 headers 和配置,支持重定向处理。
|
|
152
184
|
|
|
153
185
|
:param page: Playwright 的 Page 对象
|
|
154
186
|
:param url: 请求地址
|
|
@@ -173,17 +205,43 @@ def fetch_get(page: Page, url: str, headers: Optional[dict] = None, config: Opti
|
|
|
173
205
|
const defaultConfig = {
|
|
174
206
|
method: 'GET',
|
|
175
207
|
credentials: 'include',
|
|
176
|
-
mode: 'cors'
|
|
208
|
+
mode: 'cors',
|
|
209
|
+
redirect: 'follow' // 明确设置跟随重定向
|
|
177
210
|
};
|
|
178
211
|
|
|
179
212
|
const headers = Object.assign({}, defaultHeaders, extraHeaders || {});
|
|
180
213
|
const options = Object.assign({}, defaultConfig, config || {}, { headers: headers });
|
|
181
214
|
|
|
182
215
|
const response = await fetch(url, options);
|
|
216
|
+
|
|
217
|
+
// 处理重定向
|
|
218
|
+
if (response.redirected) {
|
|
219
|
+
console.log(`请求被重定向到: ${response.url}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
183
222
|
if (!response.ok) {
|
|
223
|
+
// 如果是重定向相关的状态码,尝试获取响应内容
|
|
224
|
+
if (response.status >= 300 && response.status < 400) {
|
|
225
|
+
const text = await response.text();
|
|
226
|
+
return {
|
|
227
|
+
"error": "redirect_error",
|
|
228
|
+
"message": `HTTP ${response.status} - ${response.statusText}`,
|
|
229
|
+
"redirect_url": response.url,
|
|
230
|
+
"response_text": text,
|
|
231
|
+
"status": response.status
|
|
232
|
+
};
|
|
233
|
+
}
|
|
184
234
|
throw new Error(`HTTP ${response.status} - ${response.statusText}`);
|
|
185
235
|
}
|
|
186
|
-
|
|
236
|
+
|
|
237
|
+
// 尝试解析 JSON,如果失败则返回文本内容
|
|
238
|
+
const contentType = response.headers.get('content-type');
|
|
239
|
+
if (contentType && contentType.includes('application/json')) {
|
|
240
|
+
return await response.json();
|
|
241
|
+
} else {
|
|
242
|
+
const text = await response.text();
|
|
243
|
+
return { "content": text, "content_type": contentType, "final_url": response.url };
|
|
244
|
+
}
|
|
187
245
|
} catch (error) {
|
|
188
246
|
return { "error": "fetch_failed", "message": error.message };
|
|
189
247
|
}
|
qrpa/shein_excel.py
CHANGED
|
@@ -195,7 +195,6 @@ class SheinExcel:
|
|
|
195
195
|
dict = read_dict_from_file(cache_file)
|
|
196
196
|
for store_username, shein_back_list in dict.items():
|
|
197
197
|
for item in shein_back_list:
|
|
198
|
-
|
|
199
198
|
store_name = dict_store.get(store_username)
|
|
200
199
|
|
|
201
200
|
returnOrderId = item['id']
|
|
@@ -297,7 +296,6 @@ class SheinExcel:
|
|
|
297
296
|
dict = read_dict_from_file(cache_file)
|
|
298
297
|
for store_username, shein_back_list in dict.items():
|
|
299
298
|
for item in shein_back_list:
|
|
300
|
-
|
|
301
299
|
store_name = dict_store.get(store_username)
|
|
302
300
|
|
|
303
301
|
returnOrderId = item['id']
|
|
@@ -393,7 +391,6 @@ class SheinExcel:
|
|
|
393
391
|
dict = read_dict_from_file(cache_file)
|
|
394
392
|
for store_username, shein_back_list in dict.items():
|
|
395
393
|
for item in shein_back_list:
|
|
396
|
-
|
|
397
394
|
store_name = dict_store.get(store_username)
|
|
398
395
|
|
|
399
396
|
returnOrderId = item['id']
|
|
@@ -1209,9 +1206,9 @@ class SheinExcel:
|
|
|
1209
1206
|
excel_path_month = str(self.config.excel_shein_finance_month_report).replace('#store_name#', store_name)
|
|
1210
1207
|
|
|
1211
1208
|
month_data = [[
|
|
1212
|
-
'平台SKU', '商家SKU', '属性集', '数量', '单价', '金额', '单价成本', '利润', '售出数量', '售出金额', '添加时间',
|
|
1209
|
+
'平台SKU', '商家SKU', '属性集', '数量', '单价', '金额', '单价成本', '利润', '售出数量', '售出金额', '售出成本', '添加时间',
|
|
1213
1210
|
'业务单号', '单据号', '变动类型', '结算类型', 'SKC', '供方货号', '供应商名称', 'SKU图片',
|
|
1214
|
-
], ['合计', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']]
|
|
1211
|
+
], ['合计', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']]
|
|
1215
1212
|
log('len(ledger_list)', len(ledger_list))
|
|
1216
1213
|
for month_item in ledger_list:
|
|
1217
1214
|
row_item = []
|
|
@@ -1230,6 +1227,7 @@ class SheinExcel:
|
|
|
1230
1227
|
month_item['quantity'] if month_item['cost_price'] and month_item['settleTypeName'] == '收入结算' else 0)
|
|
1231
1228
|
row_item.append(
|
|
1232
1229
|
month_item['amount'] if month_item['cost_price'] and month_item['settleTypeName'] == '收入结算' else 0)
|
|
1230
|
+
row_item.append('')
|
|
1233
1231
|
row_item.append(month_item['addTime'])
|
|
1234
1232
|
row_item.append(month_item['businessNo'])
|
|
1235
1233
|
row_item.append(month_item['billNo'])
|
|
@@ -1250,10 +1248,11 @@ class SheinExcel:
|
|
|
1250
1248
|
add_borders(sheet)
|
|
1251
1249
|
format_to_money(sheet, ['单价', '金额', '利润'])
|
|
1252
1250
|
format_to_datetime(sheet, ['时间'])
|
|
1253
|
-
add_formula_for_column(sheet, '利润', '=IF(AND(ISNUMBER(G3),
|
|
1254
|
-
add_formula_for_column(sheet, '售出数量', '=IF(AND(ISNUMBER(G3),
|
|
1255
|
-
add_formula_for_column(sheet, '售出金额', '=IF(AND(ISNUMBER(G3),
|
|
1256
|
-
|
|
1251
|
+
add_formula_for_column(sheet, '利润', '=IF(AND(ISNUMBER(G3),P3="收入结算"),F3-G3*D3,0)', 3)
|
|
1252
|
+
add_formula_for_column(sheet, '售出数量', '=IF(AND(ISNUMBER(G3),P3="收入结算"),D3,0)', 3)
|
|
1253
|
+
add_formula_for_column(sheet, '售出金额', '=IF(AND(ISNUMBER(G3),P3="收入结算"),F3,0)', 3)
|
|
1254
|
+
add_formula_for_column(sheet, '售出成本', '=IF(AND(ISNUMBER(G3),P3="收入结算"),D3 * G3,0)', 3)
|
|
1255
|
+
add_sum_for_cell(sheet, ['数量', '金额', '利润', '售出数量', '售出金额', '售出成本'])
|
|
1257
1256
|
column_to_left(sheet, ['平台SKU', '商家SKU', '属性集'])
|
|
1258
1257
|
column_to_right(sheet, ['单价', '金额', '利润'])
|
|
1259
1258
|
hidden_columns(sheet, ['SKU图片'])
|
|
@@ -1370,7 +1369,7 @@ class SheinExcel:
|
|
|
1370
1369
|
|
|
1371
1370
|
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])
|
|
1372
1371
|
|
|
1373
|
-
write_data(excel_path_month, sheet_name, replenish_data)
|
|
1372
|
+
write_data(excel_path_month, sheet_name, sort_by_column(replenish_data, 2, 2))
|
|
1374
1373
|
|
|
1375
1374
|
app, wb, sheet = open_excel(excel_path_month, sheet_name)
|
|
1376
1375
|
set_title_style(sheet, 2)
|
|
@@ -1540,19 +1539,28 @@ class SheinExcel:
|
|
|
1540
1539
|
|
|
1541
1540
|
target_month = find_column_by_data(sheet, 2, last_month)
|
|
1542
1541
|
sheet.range(f'{target_month}3').value = f"='{last_month}月销售明细'!I2"
|
|
1542
|
+
|
|
1543
1543
|
sheet.range(f'{target_month}4').number_format = f"¥#,##0.00;¥-#,##0.00"
|
|
1544
1544
|
sheet.range(f'{target_month}4').value = f"='{last_month}月销售明细'!J2"
|
|
1545
|
+
|
|
1546
|
+
sheet.range(f'A5').value = f"销售成本"
|
|
1545
1547
|
sheet.range(f'{target_month}5').number_format = f"¥#,##0.00;¥-#,##0.00"
|
|
1546
|
-
sheet.range(f'{target_month}5').value = f"='{last_month}月销售明细'!
|
|
1548
|
+
sheet.range(f'{target_month}5').value = f"='{last_month}月销售明细'!K2"
|
|
1549
|
+
|
|
1550
|
+
sheet.range(f'A6').value = f"销售利润"
|
|
1547
1551
|
sheet.range(f'{target_month}6').number_format = f"¥#,##0.00;¥-#,##0.00"
|
|
1552
|
+
sheet.range(f'{target_month}6').value = f"='{last_month}月销售明细'!H2"
|
|
1553
|
+
|
|
1554
|
+
# sheet.range(f'{target_month}6').number_format = f"¥#,##0.00;¥-#,##0.00"
|
|
1548
1555
|
# sheet.range(f'{target_month}6').value = f"=-'{last_month}月退货与报废单列表'!L2 * 3"
|
|
1549
1556
|
sheet.range(f'{target_month}7').number_format = f"¥#,##0.00;¥-#,##0.00"
|
|
1550
1557
|
sheet.range(f'{target_month}7').value = f"=-'{last_month}月补扣款列表'!H2"
|
|
1551
1558
|
sheet.range(f'{target_month}8').number_format = f"¥#,##0.00;¥-#,##0.00"
|
|
1552
1559
|
sheet.range(f'{target_month}9').number_format = f"¥#,##0.00;¥-#,##0.00"
|
|
1553
|
-
sheet.range(f'{target_month}9').value = f"=SUM({target_month}
|
|
1560
|
+
sheet.range(f'{target_month}9').value = f"=SUM({target_month}6:{target_month}8)"
|
|
1554
1561
|
sheet.range(f'{target_month}10').number_format = f"¥#,##0.00;¥-#,##0.00"
|
|
1555
|
-
sheet.range(f'{target_month}10').value = f"='{last_month}月库存结余'!
|
|
1562
|
+
sheet.range(f'{target_month}10').value = f"='{last_month}月库存结余'!Q2"
|
|
1563
|
+
|
|
1556
1564
|
sheet.range('A1').value = f'2025年{last_month}月 shein 利润汇总表 {store_name}'
|
|
1557
1565
|
sheet.range(f'{target_month}:{target_month}').autofit()
|
|
1558
1566
|
wb.save()
|
|
@@ -2442,7 +2450,7 @@ class SheinExcel:
|
|
|
2442
2450
|
self.dealFormula(sheet) # 有空再封装优化
|
|
2443
2451
|
colorize_by_field(sheet, 'SPU')
|
|
2444
2452
|
autofit_column(sheet, ['商品信息', '店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
|
|
2445
|
-
column_to_left(sheet, ['店铺名称', 'SKC点击率/SKC转化率', '自主参与活动','近7天SKU销量/SKC销量/SKC曝光'])
|
|
2453
|
+
column_to_left(sheet, ['店铺名称', 'SKC点击率/SKC转化率', '自主参与活动', '近7天SKU销量/SKC销量/SKC曝光'])
|
|
2446
2454
|
specify_column_width(sheet, ['商品标题'], 150 / 6)
|
|
2447
2455
|
add_borders(sheet)
|
|
2448
2456
|
InsertImageV2(sheet, ['SKC图片', 'SKU图片'], 'shein', 120, None, None, True)
|
|
@@ -2530,3 +2538,112 @@ class SheinExcel:
|
|
|
2530
2538
|
sheet.range(rangeF).number_format = '0.00'
|
|
2531
2539
|
sheet.range(rangeG).formula = f'=IF(ISNUMBER({rangeB}),{col_week_1}{row}*{col_gross_profit}{row},"")'
|
|
2532
2540
|
sheet.range(rangeG).number_format = '0.00'
|
|
2541
|
+
|
|
2542
|
+
def write_check_order(self, erp, start_date, end_date):
|
|
2543
|
+
header = ['店铺账号', '店铺别名', '店长', '报账单号', '货号', 'SKC', '平台SKU', '商家SKU', '属性集', '商品数量', '账单类型', '收支类型', '状态', '币种', '金额', 'ERP成本',
|
|
2544
|
+
'成本总额', '业务单号', '费用类型', '备注', '来源单号', '账单创建时间', '台账添加时间', '报账时间', '预计结算日期', '实际结算日期']
|
|
2545
|
+
excel_data = [header]
|
|
2546
|
+
|
|
2547
|
+
dict_store = read_dict_from_file(self.config.shein_store_alias)
|
|
2548
|
+
|
|
2549
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/check_order_{start_date}_{end_date}.json'
|
|
2550
|
+
dict = read_dict_from_file(cache_file)
|
|
2551
|
+
for store_username, data_list in dict.items():
|
|
2552
|
+
for item in data_list:
|
|
2553
|
+
store_name = dict_store.get(store_username)
|
|
2554
|
+
store_manager = self.config.shein_store_manager.get(str(store_username).lower())
|
|
2555
|
+
|
|
2556
|
+
row_item = []
|
|
2557
|
+
row_item.append(store_username)
|
|
2558
|
+
row_item.append(store_name)
|
|
2559
|
+
row_item.append(store_manager)
|
|
2560
|
+
row_item.append(item['reportOrderNo'])
|
|
2561
|
+
row_item.append(item['goodsSn'])
|
|
2562
|
+
row_item.append(item['skcName'])
|
|
2563
|
+
row_item.append(item['skuCode'])
|
|
2564
|
+
row_item.append(item['skuSn'])
|
|
2565
|
+
row_item.append(item['suffix'])
|
|
2566
|
+
row_item.append(item['goodsCount'])
|
|
2567
|
+
row_item.append(item['secondOrderTypeName'])
|
|
2568
|
+
row_item.append(item['inAndOutName'])
|
|
2569
|
+
row_item.append(item['settlementStatusName'])
|
|
2570
|
+
row_item.append(item['settleCurrencyCode'])
|
|
2571
|
+
row_item.append(item['income'])
|
|
2572
|
+
row_item.append(self.bridge.get_sku_cost(item['skuSn'], erp))
|
|
2573
|
+
row_item.append('')
|
|
2574
|
+
row_item.append(item['bzOrderNo'])
|
|
2575
|
+
row_item.append(item['expenseTypeName'])
|
|
2576
|
+
row_item.append(item['remark'])
|
|
2577
|
+
row_item.append(item['sourceNo'])
|
|
2578
|
+
row_item.append(item['addTime'])
|
|
2579
|
+
row_item.append(item['businessCompletedTime'])
|
|
2580
|
+
row_item.append(item['reportTime'])
|
|
2581
|
+
row_item.append(item['estimatePayTime'])
|
|
2582
|
+
row_item.append(item['completedPayTime'])
|
|
2583
|
+
|
|
2584
|
+
excel_data.append(row_item)
|
|
2585
|
+
|
|
2586
|
+
cache_file_excel = f'{self.config.auto_dir}/shein/cache/shein_return_order_list_excel_{start_date}_{end_date}.json'
|
|
2587
|
+
write_dict_to_file(cache_file_excel, excel_data)
|
|
2588
|
+
|
|
2589
|
+
sheet_name = '收支明细'
|
|
2590
|
+
batch_excel_operations(self.config.excel_shein_finance_month_report_pop, [
|
|
2591
|
+
(sheet_name, 'write', excel_data, ['R']),
|
|
2592
|
+
(sheet_name, 'format', self.format_check_order)
|
|
2593
|
+
])
|
|
2594
|
+
|
|
2595
|
+
header = ['店铺账号', '店铺别名', '店长', '出库金额', '出库成本', '备货作业费', '代收服务费', '订单履约服务费', '订单退货', '退货处理费', '退货单履约服务费', '利润']
|
|
2596
|
+
excel_data = [header]
|
|
2597
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/check_order_{start_date}_{end_date}.json'
|
|
2598
|
+
dict = read_dict_from_file(cache_file)
|
|
2599
|
+
for store_username, data_list in dict.items():
|
|
2600
|
+
store_name = dict_store.get(store_username)
|
|
2601
|
+
store_manager = self.config.shein_store_manager.get(str(store_username).lower())
|
|
2602
|
+
row_item = []
|
|
2603
|
+
row_item.append(store_username)
|
|
2604
|
+
row_item.append(store_name)
|
|
2605
|
+
row_item.append(store_manager)
|
|
2606
|
+
row_item.append('')
|
|
2607
|
+
row_item.append('')
|
|
2608
|
+
row_item.append('')
|
|
2609
|
+
row_item.append('')
|
|
2610
|
+
row_item.append('')
|
|
2611
|
+
row_item.append('')
|
|
2612
|
+
row_item.append('')
|
|
2613
|
+
row_item.append('')
|
|
2614
|
+
row_item.append('')
|
|
2615
|
+
excel_data.append(row_item)
|
|
2616
|
+
|
|
2617
|
+
sheet_name = '总表'
|
|
2618
|
+
batch_excel_operations(self.config.excel_shein_finance_month_report_pop, [
|
|
2619
|
+
(sheet_name, 'write', excel_data),
|
|
2620
|
+
(sheet_name, 'format', self.format_check_order),
|
|
2621
|
+
('Sheet1', 'delete'),
|
|
2622
|
+
(sheet_name, 'move', 1),
|
|
2623
|
+
])
|
|
2624
|
+
|
|
2625
|
+
def format_check_order(self, sheet):
|
|
2626
|
+
if sheet.name == '收支明细':
|
|
2627
|
+
beautify_title(sheet)
|
|
2628
|
+
add_borders(sheet)
|
|
2629
|
+
format_to_datetime(sheet, ['时间'])
|
|
2630
|
+
format_to_date(sheet, ['日期'])
|
|
2631
|
+
format_to_money(sheet, ['金额', '成本'])
|
|
2632
|
+
column_to_right(sheet, ['金额', '成本'])
|
|
2633
|
+
column_to_left(sheet, ['货号', '商家SKU'])
|
|
2634
|
+
add_formula_for_column(sheet, '成本总额', '=IF(ISNUMBER(O2),O2*J2,0)')
|
|
2635
|
+
|
|
2636
|
+
if sheet.name == '总表':
|
|
2637
|
+
beautify_title(sheet)
|
|
2638
|
+
add_borders(sheet)
|
|
2639
|
+
format_to_money(sheet, ['金额', '成本', '费', '订单退货', '利润'])
|
|
2640
|
+
column_to_right(sheet, ['金额', '成本', '费', '订单退货', '利润'])
|
|
2641
|
+
add_formula_for_column(sheet, '出库金额', '=SUMIFS(收支明细!O:O,收支明细!A:A,A2,收支明细!L:L,"收入")')
|
|
2642
|
+
add_formula_for_column(sheet, '出库成本', '=SUMIFS(收支明细!P:P,收支明细!A:A,A2,收支明细!L:L,"收入")')
|
|
2643
|
+
add_formula_for_column(sheet, '备货作业费', '=SUMIFS(收支明细!O:O,收支明细!A:A,A2,收支明细!L:L,"支出",收支明细!K:K,"备货作业费")')
|
|
2644
|
+
add_formula_for_column(sheet, '代收服务费', '=SUMIFS(收支明细!O:O,收支明细!A:A,A2,收支明细!L:L,"支出",收支明细!K:K,"代收服务费")')
|
|
2645
|
+
add_formula_for_column(sheet, '订单履约服务费', '=SUMIFS(收支明细!O:O,收支明细!A:A,A2,收支明细!L:L,"支出",收支明细!K:K,"订单履约服务费")')
|
|
2646
|
+
add_formula_for_column(sheet, '订单退货', '=SUMIFS(收支明细!O:O,收支明细!A:A,A2,收支明细!L:L,"支出",收支明细!K:K,"订单退货")')
|
|
2647
|
+
add_formula_for_column(sheet, '退货处理费', '=SUMIFS(收支明细!O:O,收支明细!A:A,A2,收支明细!L:L,"支出",收支明细!K:K,"退货处理费")')
|
|
2648
|
+
add_formula_for_column(sheet, '退货单履约服务费', '=SUMIFS(收支明细!O:O,收支明细!A:A,A2,收支明细!L:L,"支出",收支明细!K:K,"退货单履约服务费")')
|
|
2649
|
+
add_formula_for_column(sheet, '利润', '=D2-E2-F2-G2-H2-I2-J2')
|
qrpa/shein_lib.py
CHANGED
|
@@ -26,6 +26,7 @@ class SheinLib:
|
|
|
26
26
|
self.DictQueryTime = {}
|
|
27
27
|
|
|
28
28
|
self.deal_auth()
|
|
29
|
+
# self.get_user()
|
|
29
30
|
|
|
30
31
|
# 处理鉴权
|
|
31
32
|
def deal_auth(self):
|
|
@@ -101,6 +102,10 @@ class SheinLib:
|
|
|
101
102
|
if "crashed" in str(status_error):
|
|
102
103
|
break
|
|
103
104
|
|
|
105
|
+
if web_page.locator('xpath=//div[text()="扫码登录"]').is_visible():
|
|
106
|
+
log('检查到扫码登录,切换至账号登录', self.store_username, self.store_name)
|
|
107
|
+
web_page.locator('xpath=//*[@id="container"]/div[2]/div[4]/img').click()
|
|
108
|
+
|
|
104
109
|
if web_page.locator('xpath=//div[@id="container" and @alita-name="gmpsso"]//button[@type="button" and @id]').nth(0).is_visible():
|
|
105
110
|
if 'https://sso.geiwohuo.com/#/home' not in web_page.url:
|
|
106
111
|
log("鉴权确定按钮可见 点击'确定'按钮", web_page.title(), web_page.url, self.store_username, self.store_name)
|
|
@@ -2889,3 +2894,45 @@ class SheinLib:
|
|
|
2889
2894
|
write_dict_to_file_ex(cache_file, {self.store_name: NotifyItem}, {self.store_name})
|
|
2890
2895
|
|
|
2891
2896
|
return excel_data
|
|
2897
|
+
|
|
2898
|
+
def check_order_list(self, source, first_day, last_day):
|
|
2899
|
+
page_num = 1
|
|
2900
|
+
page_size = 200 # 列表最多返回200条数据 大了没有用
|
|
2901
|
+
|
|
2902
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/check_order_{first_day}_{last_day}.json'
|
|
2903
|
+
list_item = read_dict_from_file_ex(cache_file, self.store_username)
|
|
2904
|
+
|
|
2905
|
+
url = f"https://sso.geiwohuo.com/gsfs/finance/reportOrder/dualMode/checkOrderList/item/union"
|
|
2906
|
+
payload = {
|
|
2907
|
+
"page" : page_num,
|
|
2908
|
+
"perPage" : page_size,
|
|
2909
|
+
"detailAddTimeStart": f"{first_day} 00:00:00",
|
|
2910
|
+
"detailAddTimeEnd" : f"{last_day} 23:59:59"
|
|
2911
|
+
}
|
|
2912
|
+
response_text = fetch(self.web_page, url, payload)
|
|
2913
|
+
error_code = response_text.get('code')
|
|
2914
|
+
if str(error_code) != '0':
|
|
2915
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
2916
|
+
list_item = response_text['info']['data']
|
|
2917
|
+
total = response_text['info']['meta']['count']
|
|
2918
|
+
totalPage = math.ceil(total / page_size)
|
|
2919
|
+
|
|
2920
|
+
if int(total) == len(list_item):
|
|
2921
|
+
log('总数与缓存数量相同 跳过剩余页抓取', total)
|
|
2922
|
+
return list_item
|
|
2923
|
+
|
|
2924
|
+
for page in range(2, totalPage + 1):
|
|
2925
|
+
log(f'获取收支明细列表 第{page}/{totalPage}页')
|
|
2926
|
+
payload['pageNumber'] = page
|
|
2927
|
+
response_text = fetch(self.web_page, url, payload)
|
|
2928
|
+
spu_list_new = response_text['info']['data']
|
|
2929
|
+
list_item += spu_list_new
|
|
2930
|
+
time.sleep(0.1)
|
|
2931
|
+
|
|
2932
|
+
for item in list_item:
|
|
2933
|
+
supplierSku = item['skuSn']
|
|
2934
|
+
item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
|
|
2935
|
+
item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
|
|
2936
|
+
|
|
2937
|
+
write_dict_to_file_ex(cache_file, {self.store_username: list_item}, [self.store_username])
|
|
2938
|
+
return list_item
|
qrpa/shein_mysql.py
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
from .mysql_module.shein_return_order_model import SheinReturnOrderManager
|
|
4
|
-
from .mysql_module.shein_product_model import SheinProductManager
|
|
5
|
-
from .fun_base import log
|
|
6
|
-
|
|
7
|
-
import os
|
|
8
|
-
|
|
9
|
-
class SheinMysql:
|
|
10
|
-
def __init__(self, config):
|
|
11
|
-
self.config = config
|
|
12
|
-
|
|
13
|
-
def upsert_shein_return_order(self, json_file):
|
|
14
|
-
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
15
|
-
# 创建管理器实例
|
|
16
|
-
manager = SheinReturnOrderManager(self.config.db.database_url)
|
|
17
|
-
# 创建数据表
|
|
18
|
-
manager.create_tables()
|
|
19
|
-
# 读取JSON文件
|
|
20
|
-
with open(json_file, 'r', encoding='utf-8') as f:
|
|
21
|
-
dict = json.load(f)
|
|
22
|
-
for store_username, data_list in dict.items():
|
|
23
|
-
manager.upsert_return_order_data(store_username, data_list)
|
|
24
|
-
|
|
25
|
-
def upsert_shein_product(self, json_file):
|
|
26
|
-
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
27
|
-
# 创建管理器实例
|
|
28
|
-
manager = SheinProductManager(self.config.db.database_url)
|
|
29
|
-
# 创建数据表
|
|
30
|
-
manager.create_tables()
|
|
31
|
-
with open(json_file, 'r', encoding='utf-8') as f:
|
|
32
|
-
file_list = json.load(f)
|
|
33
|
-
for store_username, store_skc_list_file in file_list.items():
|
|
34
|
-
with open(store_skc_list_file, 'r', encoding='utf-8') as f:
|
|
35
|
-
dict_store_skc_list = json.load(f)
|
|
36
|
-
for store_username, data_list in dict_store_skc_list.items():
|
|
37
|
-
manager.upsert_product_data(data_list)
|
|
38
|
-
|
|
39
|
-
def upsert_shein_product_info(self, json_file):
|
|
40
|
-
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
41
|
-
# 创建管理器实例
|
|
42
|
-
manager = SheinProductManager(self.config.db.database_url)
|
|
43
|
-
# 创建数据表
|
|
44
|
-
manager.create_tables()
|
|
45
|
-
with open(json_file, 'r', encoding='utf-8') as f:
|
|
46
|
-
file_list = json.load(f)
|
|
47
|
-
for store_username, store_spu_list in file_list.items():
|
|
48
|
-
for spu in store_spu_list:
|
|
49
|
-
product_detail_file = f'{self.config.auto_dir}/shein/product_detail/product_detail_{spu}.json'
|
|
50
|
-
attribute_file = f'{self.config.auto_dir}/shein/attribute/attribute_template_{spu}.json'
|
|
51
|
-
if os.path.exists(product_detail_file):
|
|
52
|
-
with open(product_detail_file, 'r', encoding='utf-8') as f:
|
|
53
|
-
data_list = json.load(f)
|
|
54
|
-
manager.upsert_product_detail(spu, 'product_detail', data_list)
|
|
55
|
-
else:
|
|
56
|
-
log(f'文件不存在: {product_detail_file}')
|
|
57
|
-
if os.path.exists(attribute_file):
|
|
58
|
-
with open(attribute_file, 'r', encoding='utf-8') as f:
|
|
59
|
-
data_list = json.load(f)
|
|
60
|
-
manager.upsert_product_detail(spu, 'attribute_template', data_list)
|
|
61
|
-
else:
|
|
62
|
-
log(f'文件不存在: {attribute_file}')
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from .mysql_module.shein_return_order_model import SheinReturnOrderManager
|
|
4
|
+
from .mysql_module.shein_product_model import SheinProductManager
|
|
5
|
+
from .fun_base import log
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
class SheinMysql:
|
|
10
|
+
def __init__(self, config):
|
|
11
|
+
self.config = config
|
|
12
|
+
|
|
13
|
+
def upsert_shein_return_order(self, json_file):
|
|
14
|
+
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
15
|
+
# 创建管理器实例
|
|
16
|
+
manager = SheinReturnOrderManager(self.config.db.database_url)
|
|
17
|
+
# 创建数据表
|
|
18
|
+
manager.create_tables()
|
|
19
|
+
# 读取JSON文件
|
|
20
|
+
with open(json_file, 'r', encoding='utf-8') as f:
|
|
21
|
+
dict = json.load(f)
|
|
22
|
+
for store_username, data_list in dict.items():
|
|
23
|
+
manager.upsert_return_order_data(store_username, data_list)
|
|
24
|
+
|
|
25
|
+
def upsert_shein_product(self, json_file):
|
|
26
|
+
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
27
|
+
# 创建管理器实例
|
|
28
|
+
manager = SheinProductManager(self.config.db.database_url)
|
|
29
|
+
# 创建数据表
|
|
30
|
+
manager.create_tables()
|
|
31
|
+
with open(json_file, 'r', encoding='utf-8') as f:
|
|
32
|
+
file_list = json.load(f)
|
|
33
|
+
for store_username, store_skc_list_file in file_list.items():
|
|
34
|
+
with open(store_skc_list_file, 'r', encoding='utf-8') as f:
|
|
35
|
+
dict_store_skc_list = json.load(f)
|
|
36
|
+
for store_username, data_list in dict_store_skc_list.items():
|
|
37
|
+
manager.upsert_product_data(data_list)
|
|
38
|
+
|
|
39
|
+
def upsert_shein_product_info(self, json_file):
|
|
40
|
+
log(f'当前使用的数据库: {self.config.db.database_url}')
|
|
41
|
+
# 创建管理器实例
|
|
42
|
+
manager = SheinProductManager(self.config.db.database_url)
|
|
43
|
+
# 创建数据表
|
|
44
|
+
manager.create_tables()
|
|
45
|
+
with open(json_file, 'r', encoding='utf-8') as f:
|
|
46
|
+
file_list = json.load(f)
|
|
47
|
+
for store_username, store_spu_list in file_list.items():
|
|
48
|
+
for spu in store_spu_list:
|
|
49
|
+
product_detail_file = f'{self.config.auto_dir}/shein/product_detail/product_detail_{spu}.json'
|
|
50
|
+
attribute_file = f'{self.config.auto_dir}/shein/attribute/attribute_template_{spu}.json'
|
|
51
|
+
if os.path.exists(product_detail_file):
|
|
52
|
+
with open(product_detail_file, 'r', encoding='utf-8') as f:
|
|
53
|
+
data_list = json.load(f)
|
|
54
|
+
manager.upsert_product_detail(spu, 'product_detail', data_list)
|
|
55
|
+
else:
|
|
56
|
+
log(f'文件不存在: {product_detail_file}')
|
|
57
|
+
if os.path.exists(attribute_file):
|
|
58
|
+
with open(attribute_file, 'r', encoding='utf-8') as f:
|
|
59
|
+
data_list = json.load(f)
|
|
60
|
+
manager.upsert_product_detail(spu, 'attribute_template', data_list)
|
|
61
|
+
else:
|
|
62
|
+
log(f'文件不存在: {attribute_file}')
|
qrpa/shein_ziniao.py
CHANGED
|
@@ -488,7 +488,7 @@ class ZiniaoRunner:
|
|
|
488
488
|
raise RuntimeError("店铺列表为空")
|
|
489
489
|
|
|
490
490
|
# 多线程并发执行任务
|
|
491
|
-
max_threads =
|
|
491
|
+
max_threads = 3 if (hostname().lower() == 'krrpa' or hostname().lower() == 'jyrpa') else 3
|
|
492
492
|
log(f'当前启用线程数: {max_threads}')
|
|
493
493
|
self.task_manager.run_with_thread_pool(browser_list, max_threads, run, task_key, just_store_username, is_skip_store)
|
|
494
494
|
|