qrpa 1.1.38__py3-none-any.whl → 1.1.40__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of qrpa might be problematic. Click here for more details.

qrpa/shein_excel.py CHANGED
@@ -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
 
@@ -2938,7 +2939,7 @@ class SheinExcel:
2938
2939
  # 在"金额"列后面插入两列
2939
2940
  new_header = header[:amount_col_idx + 1] + ['ERP成本', '成本总额'] + header[amount_col_idx + 1:]
2940
2941
  log(f'在第{amount_col_idx + 1}列(金额)后面插入"ERP成本"和"成本总额"两列')
2941
-
2942
+
2942
2943
  # 业务单号列在插入新列后的索引需要调整
2943
2944
  if business_no_col_idx is not None and business_no_col_idx > amount_col_idx:
2944
2945
  business_no_col_idx_adjusted = business_no_col_idx + 2 # 因为插入了2列
@@ -3005,7 +3006,7 @@ class SheinExcel:
3005
3006
  # 查找需要格式化为文本的列
3006
3007
  text_format_columns = []
3007
3008
  str_keywords = ['业务单号']
3008
-
3009
+
3009
3010
  def col_idx_to_letter(idx):
3010
3011
  """将列索引转换为Excel列字母 (0->A, 1->B, ..., 25->Z, 26->AA, ...)"""
3011
3012
  result = ''
@@ -3015,7 +3016,7 @@ class SheinExcel:
3015
3016
  result = chr(65 + idx % 26) + result
3016
3017
  idx //= 26
3017
3018
  return result
3018
-
3019
+
3019
3020
  for col_idx, col_name in enumerate(header):
3020
3021
  col_name_str = str(col_name)
3021
3022
  # 检查列名是否包含需要保持为文本的关键词
@@ -3023,7 +3024,7 @@ class SheinExcel:
3023
3024
  col_letter = col_idx_to_letter(col_idx)
3024
3025
  text_format_columns.append(col_letter)
3025
3026
  log(f'列"{col_name}"(第{col_idx}列,Excel列{col_letter})将格式化为文本')
3026
-
3027
+
3027
3028
  log(f'共{len(text_format_columns)}列需要格式化为文本: {text_format_columns}')
3028
3029
 
3029
3030
  # 使用batch_excel_operations批量写入和格式化
@@ -3123,3 +3124,154 @@ class SheinExcel:
3123
3124
  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},"退货单履约服务费")')
3124
3125
 
3125
3126
  add_formula_for_column(sheet, '利润', '=D2-E2-F2-G2-H2-I2-J2-K2')
3127
+
3128
+ def write_vssv_order_list(self):
3129
+ """
3130
+ 写入VSSV增值服务订单列表到Excel
3131
+ """
3132
+ # 获取上个月的时间范围
3133
+ first_day, last_day = TimeUtils.get_last_month_range()
3134
+ last_month = TimeUtils.get_last_month()
3135
+
3136
+ # 读取店铺别名映射
3137
+ dict_store = read_dict_from_file(self.config.shein_store_alias)
3138
+
3139
+ # 准备Excel数据
3140
+ header = ['店铺账号', '店铺名称', '增值服务订单号', '增值服务单号', '采购订单号',
3141
+ '平台SKC', '商家SKC', 'SKC数量','扣款单号', '订单状态', '实际总金额',
3142
+ '增值服务项', '创建时间', '完成时间']
3143
+ excel_data = [header]
3144
+
3145
+ # 遍历vssv_order目录下的所有店铺数据
3146
+ src_directory = f'{self.config.auto_dir}/shein/vssv_order'
3147
+
3148
+ if not os.path.exists(src_directory):
3149
+ log(f'VSSV订单目录不存在: {src_directory}')
3150
+ return
3151
+
3152
+ for entry in os.listdir(src_directory):
3153
+ # 检查是否为匹配的缓存文件
3154
+ if entry.startswith(f"vssv_order_list_") and entry.endswith(f"_{first_day}_{last_day}.json"):
3155
+ file_path = os.path.join(src_directory, entry)
3156
+
3157
+ # 从文件名中提取店铺账号
3158
+ # 格式: vssv_order_list_{store_username}_{first_day}_{last_day}.json
3159
+ parts = entry.replace('.json', '').split('_')
3160
+ if len(parts) >= 5:
3161
+ # vssv_order_list_{store_username}_{first_day}_{last_day}
3162
+ # parts[0]='vssv', parts[1]='order', parts[2]='list', parts[3]=store_username
3163
+ store_username = parts[3]
3164
+ else:
3165
+ log(f'无法解析店铺账号: {entry}')
3166
+ continue
3167
+
3168
+ # 获取店铺名称
3169
+ store_name = dict_store.get(store_username, store_username)
3170
+
3171
+ # 读取订单数据
3172
+ order_list = read_dict_from_file(file_path)
3173
+ log(f'读取店铺 {store_name}({store_username}) 的VSSV订单: {len(order_list)}条')
3174
+
3175
+ # 处理每条订单数据
3176
+ for order in order_list:
3177
+ # 基础订单信息
3178
+ order_no = order.get('orderNo', '-')
3179
+ sub_order_no = order.get('subOrderNo', '-')
3180
+ purchase_no = order.get('purchaseNo', '-')
3181
+ skc_img_path = order.get('skcImgPath', '')
3182
+ skc = order.get('skc', '-')
3183
+ supplier_product_number = order.get('supplierProductNumber', '-')
3184
+ skc_num = order.get('skcNum', 0)
3185
+ order_state_name = order.get('orderStateName', '-')
3186
+ actual_total_amount = order.get('actualTotalAmount', 0)
3187
+
3188
+ # 提取扣款单号(从vendorRepairList数组中)
3189
+ vendor_repair_no = '-'
3190
+ vendor_repair_list = order.get('vendorRepairList', [])
3191
+ if vendor_repair_list and len(vendor_repair_list) > 0:
3192
+ vendor_repair_no = vendor_repair_list[0].get('vendorRepairNo', '-')
3193
+
3194
+ # 提取创建时间和完成时间(从orderChangeLogVo中)
3195
+ create_time = '-'
3196
+ finish_time = '-'
3197
+ order_change_log = order.get('orderChangeLogVo', [])
3198
+ for log_item in order_change_log:
3199
+ if log_item.get('operateType') == 12: # 创建时间
3200
+ create_time = log_item.get('operateTime', '-')
3201
+ elif log_item.get('operateType') == 4: # 增值订单完成时间
3202
+ finish_time = log_item.get('operateTime', '-')
3203
+
3204
+ # 获取增值服务项列表并合并成一个字符串
3205
+ service_items = order.get('subOrderServiceItemVoList', [])
3206
+ service_items_text = '-'
3207
+
3208
+ if service_items:
3209
+ # 将所有服务项合并成一个字符串,每个服务项一行
3210
+ service_lines = []
3211
+ for service_item in service_items:
3212
+ service_name = service_item.get('serviceItemName', '-')
3213
+ settlement_qty = service_item.get('settlementQuantity', 0)
3214
+ item_amount = service_item.get('itemTotalAmount', 0)
3215
+ price = service_item.get('price', 0)
3216
+ # 格式:服务项名称 | 数量 | 金额
3217
+ service_line = f"{service_name}: {settlement_qty}x{price}=¥{item_amount}"
3218
+ service_lines.append(service_line)
3219
+ service_items_text = '\n'.join(service_lines)
3220
+
3221
+ # 添加一行数据
3222
+ row_item = []
3223
+ row_item.append(store_username) # 店铺账号
3224
+ row_item.append(store_name) # 店铺名称
3225
+ row_item.append(order_no) # 增值服务订单号
3226
+ row_item.append(sub_order_no) # 增值服务单号
3227
+ row_item.append(purchase_no) # 采购订单号
3228
+ # row_item.append(skc_img_path) # SKC图片
3229
+ row_item.append(skc) # 平台SKC
3230
+ row_item.append(supplier_product_number) # 商家SKC
3231
+ row_item.append(skc_num) # SKC数量
3232
+ row_item.append(vendor_repair_no) # 扣款单号
3233
+ row_item.append(order_state_name) # 订单状态
3234
+ row_item.append(actual_total_amount) # 实际总金额
3235
+ row_item.append(service_items_text) # 增值服务项(合并)
3236
+ row_item.append(create_time) # 创建时间
3237
+ row_item.append(finish_time) # 完成时间
3238
+ excel_data.append(row_item)
3239
+
3240
+ log(f'共收集到 {len(excel_data) - 1} 条VSSV订单数据')
3241
+
3242
+ # 如果没有数据,只有表头,则不生成Excel
3243
+ if len(excel_data) <= 1:
3244
+ log('没有VSSV订单数据,跳过Excel生成')
3245
+ return
3246
+
3247
+ # 写入Excel
3248
+ excel_path = self.config.excel_path
3249
+ sheet_name = f'{last_month}月增值服务列表'
3250
+
3251
+ batch_excel_operations(excel_path, [
3252
+ (sheet_name, 'write', excel_data, ['C', 'D', 'E', 'I']), # 订单号、单号、采购单号、扣款单号格式化为文本
3253
+ (sheet_name, 'format', self.format_vssv_order_list),
3254
+ ('Sheet1', 'delete')
3255
+ ])
3256
+
3257
+ log(f'VSSV订单列表已写入: {excel_path}')
3258
+
3259
+ def format_vssv_order_list(self, sheet):
3260
+ """
3261
+ 格式化VSSV订单列表Excel
3262
+ """
3263
+ beautify_title(sheet)
3264
+ add_borders(sheet)
3265
+ format_to_money(sheet, ['金额', '总金额'])
3266
+ column_to_right(sheet, ['金额', '数量', '总金额'])
3267
+ format_to_datetime(sheet, ['时间'])
3268
+ column_to_left(sheet, ['店铺账号', '订单号', '单号', 'SKC', '增值服务项'])
3269
+ wrap_column(sheet, ['增值服务项']) # 增值服务项列自动换行
3270
+ autofit_column(sheet, ['店铺名称', '订单状态'])
3271
+ specify_column_width(sheet, ['增值服务订单号', '增值服务单号', '采购订单号', '扣款单号'], 160 / 6)
3272
+ specify_column_width(sheet, ['增值服务项'], 280 / 6) # 服务项列设置较宽
3273
+
3274
+ # 插入SKC图片
3275
+ # InsertImageV2(sheet, ['SKC图片'], 'shein', 90)
3276
+
3277
+ sheet.autofit()
qrpa/shein_lib.py CHANGED
@@ -758,6 +758,63 @@ class SheinLib:
758
758
  log(f'Bridge数据刷新完成', self.store_username, self.store_name)
759
759
  return data_list
760
760
 
761
+ def get_vssv_order_list(self):
762
+ """
763
+ 获取VSSV订单列表
764
+
765
+ Args:
766
+ web_page: 页面对象
767
+ store_username: 店铺账号
768
+ store_name: 店铺名称
769
+
770
+ Returns:
771
+ list: 订单列表
772
+ """
773
+ page_num = 1
774
+ page_size = 200
775
+ first_day, last_day = TimeUtils.get_last_month_range()
776
+
777
+ cache_file = f'{self.config.auto_dir}/shein/vssv_order/vssv_order_list_{self.store_username}_{first_day}_{last_day}.json'
778
+ list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
779
+ if len(list_item) > 0:
780
+ return list_item
781
+
782
+ url = f"https://sso.geiwohuo.com/vssv/order/page"
783
+ payload = {
784
+ "deductionStatus": "2",
785
+ "beginTime" : f"{first_day} 00:00:00",
786
+ "endTime" : f"{last_day} 23:59:59",
787
+ "pageNumber" : page_num,
788
+ "pageSize" : page_size
789
+ }
790
+ response_text = fetch(self.web_page, url, payload)
791
+ error_code = response_text.get('code')
792
+ if str(error_code) != '0':
793
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
794
+ raise
795
+ list_item = response_text['info']['list']
796
+ total = response_text['info']['count']
797
+ totalPage = math.ceil(total / page_size)
798
+
799
+ for page in range(2, totalPage + 1):
800
+ log(f'获取VSSV订单列表 第{page}/{totalPage}页')
801
+ page_num = page
802
+ payload = {
803
+ "deductionStatus": "2",
804
+ "beginTime" : f"{first_day} 00:00:00",
805
+ "endTime" : f"{last_day} 23:59:59",
806
+ "pageNumber" : page_num,
807
+ "pageSize" : page_size
808
+ }
809
+ response_text = fetch(self.web_page, url, payload)
810
+ spu_list_new = response_text['info']['list']
811
+ list_item += spu_list_new
812
+ time.sleep(0.1)
813
+
814
+ write_dict_to_file(cache_file, list_item)
815
+
816
+ return list_item
817
+
761
818
  def get_replenish_list(self):
762
819
  page_num = 1
763
820
  page_size = 50
@@ -1225,7 +1282,7 @@ class SheinLib:
1225
1282
 
1226
1283
  # 活动信息
1227
1284
  # AB实验数据
1228
-
1285
+
1229
1286
  # 预先过滤掉不需要的商品状态
1230
1287
  log(f'过滤前商品数量: {len(spu_list)}')
1231
1288
  exclude_levels = ['退供款', '自主停产', '自主下架']
qrpa/temu_lib.py CHANGED
@@ -26,6 +26,8 @@ class TemuLib:
26
26
  """使用 XPath 登录网站"""
27
27
  # 导航到登录页面
28
28
  self.web_page.goto("https://seller.kuajingmaihuo.com/login")
29
+ # fixed
30
+ self.web_page.locator('//div[text()="账号登录"]').click()
29
31
  # 输入用户名
30
32
  self.web_page.locator('//input[@id="usernameId"]').fill("")
31
33
  self.web_page.locator('//input[@id="usernameId"]').fill(username)