minor fixes
This commit is contained in:
86
销售订单自动导入.py
86
销售订单自动导入.py
@@ -15,15 +15,15 @@ from selenium.webdriver.support import expected_conditions as EC
|
|||||||
from selenium.webdriver.support.wait import WebDriverWait
|
from selenium.webdriver.support.wait import WebDriverWait
|
||||||
from selenium.webdriver.remote.webelement import WebElement
|
from selenium.webdriver.remote.webelement import WebElement
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="销售订单自动导入脚本")
|
parser = argparse.ArgumentParser(description="销售订单自动导入")
|
||||||
parser.add_argument('invoices', nargs='?', default='')
|
parser.add_argument('invoices', nargs='?', default='')
|
||||||
parser.add_argument('--xm-username', type=str, nargs='?', default='')
|
parser.add_argument('--xm-username', type=str, nargs='?', default='')
|
||||||
parser.add_argument('--xm-password', type=str, nargs='?', default='')
|
parser.add_argument('--xm-password', type=str, nargs='?', default='')
|
||||||
parser.add_argument('--vf-token', type=str, nargs='?', default='')
|
parser.add_argument('--vf-token', type=str, nargs='?', default='')
|
||||||
parser.add_argument('--xm-web-url', type=str, nargs='?', default='https://login.xiaoman.cn/login/')
|
parser.add_argument('--xm-web-url', type=str, nargs='?', default='https://login.xiaoman.cn/login/')
|
||||||
parser.add_argument('--vf-api-url', type=str, nargs='?', default='https://ultimatron-france.vosfactures.fr/')
|
parser.add_argument('--vf-api-url', type=str, nargs='?', default='')
|
||||||
parser.add_argument('-C', '--currency', type=str, nargs='?', default='USD')
|
parser.add_argument('-C', '--currency', type=str, nargs='?', default='USD')
|
||||||
parser.add_argument('-D', '--department', type=str, nargs='?', default='ULT事业部')
|
parser.add_argument('-D', '--department', type=str, nargs='?', default='')
|
||||||
parser.add_argument('-T', '--duration', type=int, nargs='?', default=None)
|
parser.add_argument('-T', '--duration', type=int, nargs='?', default=None)
|
||||||
parser.add_argument('-a', '--automation', type=str, choices=['none', 'draft', 'final'], nargs='?', default='none')
|
parser.add_argument('-a', '--automation', type=str, choices=['none', 'draft', 'final'], nargs='?', default='none')
|
||||||
parser.add_argument('-e', '--encoding', type=str, nargs='?', default='utf-8')
|
parser.add_argument('-e', '--encoding', type=str, nargs='?', default='utf-8')
|
||||||
@@ -122,29 +122,9 @@ def main(workbook=None):
|
|||||||
# 导出发票数据
|
# 导出发票数据
|
||||||
invoices = root.findall('invoice')
|
invoices = root.findall('invoice')
|
||||||
limit = len(invoices)
|
limit = len(invoices)
|
||||||
|
data = FieldArray()
|
||||||
print(f'[信息] 已读取发票数据 {limit} 条')
|
print(f'[信息] 已读取发票数据 {limit} 条')
|
||||||
|
|
||||||
# 订单导入字段
|
|
||||||
# 详情见 <https://crm.xiaoman.cn/order/importOrder>
|
|
||||||
data = FieldArray(
|
|
||||||
'订单号',
|
|
||||||
'商机号',
|
|
||||||
'订单日期',
|
|
||||||
'当前处理人',
|
|
||||||
'业绩归属部门',
|
|
||||||
'客户编号',
|
|
||||||
'币种',
|
|
||||||
'产品名称',
|
|
||||||
'产品编号',
|
|
||||||
'产品型号',
|
|
||||||
'原价',
|
|
||||||
'折扣率',
|
|
||||||
'单价',
|
|
||||||
'数量',
|
|
||||||
'产品描述',
|
|
||||||
)
|
|
||||||
|
|
||||||
for index, invoice in enumerate(invoices):
|
for index, invoice in enumerate(invoices):
|
||||||
rate = index / limit
|
rate = index / limit
|
||||||
number = text(invoice, 'number')
|
number = text(invoice, 'number')
|
||||||
@@ -164,21 +144,23 @@ def main(workbook=None):
|
|||||||
discount = float(text(position, 'discount-percent') or '0')
|
discount = float(text(position, 'discount-percent') or '0')
|
||||||
quantity = float(text(position, 'quantity') or '0')
|
quantity = float(text(position, 'quantity') or '0')
|
||||||
|
|
||||||
|
# 订单导入字段
|
||||||
|
# 详情见 <https://crm.xiaoman.cn/order/importOrder>
|
||||||
|
data.append('订单号', number)
|
||||||
data.append('商机号', relation)
|
data.append('商机号', relation)
|
||||||
data.append('订单日期', issue_date)
|
data.append('订单日期', issue_date)
|
||||||
data.append('当前处理人', category)
|
data.append('当前处理人', category)
|
||||||
data.append('业绩归属部门', args.department)
|
data.append('业绩归属部门', args.department)
|
||||||
data.append('客户编号', client)
|
data.append('客户编号', client)
|
||||||
data.append('币种', args.currency)
|
data.append('币种', args.currency)
|
||||||
data.append('订单号', number)
|
|
||||||
data.append('产品名称', product)
|
data.append('产品名称', product)
|
||||||
data.append('产品编号', None)
|
data.append('产品编号', None)
|
||||||
data.append('产品型号', code)
|
data.append('产品型号', code)
|
||||||
data.append('原价', '%.2f' % price)
|
data.append('原价', '%.2f' % price)
|
||||||
data.append('折扣率', '%g%%' % discount)
|
data.append('折扣率', '%g%%' % discount)
|
||||||
data.append('产品描述', description)
|
|
||||||
data.append('单价', '%.2f' % (price * (1 - discount / 100)))
|
data.append('单价', '%.2f' % (price * (1 - discount / 100)))
|
||||||
data.append('数量', '%g' % quantity)
|
data.append('数量', '%g' % quantity)
|
||||||
|
data.append('产品描述', description)
|
||||||
data.newrow()
|
data.newrow()
|
||||||
|
|
||||||
# 新建导入数据表
|
# 新建导入数据表
|
||||||
@@ -242,11 +224,11 @@ def main(workbook=None):
|
|||||||
|
|
||||||
def click(selector, parent=driver, condition=EC.element_to_be_clickable):
|
def click(selector, parent=driver, condition=EC.element_to_be_clickable):
|
||||||
element = locate(selector, True, parent, condition) if isinstance(selector, str) else selector
|
element = locate(selector, True, parent, condition) if isinstance(selector, str) else selector
|
||||||
counter = lambda: int(element.get_attribute('chronometer') or 0)
|
counter = lambda: int(element.get_attribute('taximeter') or 0)
|
||||||
error = False
|
error = False
|
||||||
|
|
||||||
value = counter()
|
value = counter()
|
||||||
driver.execute_script("arguments[0].addEventListener('click', () => arguments[0].setAttribute('chronometer', arguments[1] + 1));", element, value)
|
driver.execute_script("arguments[0].addEventListener('click', () => arguments[0].setAttribute('taximeter', arguments[1] + 1));", element, value)
|
||||||
|
|
||||||
for attempt in range(args.retry):
|
for attempt in range(args.retry):
|
||||||
try:
|
try:
|
||||||
@@ -258,17 +240,18 @@ def main(workbook=None):
|
|||||||
error = True
|
error = True
|
||||||
continue
|
continue
|
||||||
# 检测点击事件
|
# 检测点击事件
|
||||||
try: WebDriverWait(driver, args.interval).until(lambda _: counter() > value)
|
try:
|
||||||
except TimeoutException: continue
|
WebDriverWait(driver, args.interval).until(lambda _: counter() > value)
|
||||||
except: pass
|
|
||||||
break
|
break
|
||||||
|
except TimeoutException: continue
|
||||||
|
except: break
|
||||||
|
|
||||||
def ready(driver):
|
def ready(driver):
|
||||||
try:
|
try:
|
||||||
condition = lambda x: 'nprogress-busy' in x.find_element(By.TAG_NAME, 'html').get_attribute('class')
|
condition = lambda x: 'nprogress-busy' in x.find_element(By.TAG_NAME, 'html').get_attribute('class')
|
||||||
wait = WebDriverWait(driver, timeout=args.interval)
|
wait = WebDriverWait(driver, timeout=args.interval)
|
||||||
wait.until(condition)
|
wait.until(condition)
|
||||||
except TimeoutException:
|
except (TimeoutException, StaleElementReferenceException):
|
||||||
pass
|
pass
|
||||||
wait = WebDriverWait(driver, timeout=args.timeout)
|
wait = WebDriverWait(driver, timeout=args.timeout)
|
||||||
wait.until_not(condition)
|
wait.until_not(condition)
|
||||||
@@ -360,23 +343,25 @@ def main(workbook=None):
|
|||||||
except:
|
except:
|
||||||
break
|
break
|
||||||
|
|
||||||
try:
|
|
||||||
driver.execute_script("arguments[0].click();", link)
|
|
||||||
driver.switch_to.window(driver.window_handles[3])
|
|
||||||
# 编辑订单
|
# 编辑订单
|
||||||
|
try:
|
||||||
|
click(link)
|
||||||
|
driver.switch_to.window(driver.window_handles[3])
|
||||||
click(".component-detail-frame-header .okki-space-item:nth-child(1) button")
|
click(".component-detail-frame-header .okki-space-item:nth-child(1) button")
|
||||||
|
|
||||||
warn = False
|
warn = False
|
||||||
|
associative = False
|
||||||
header = locate(".order-edit-header .edit-order-no span span:nth-child(1)")
|
header = locate(".order-edit-header .edit-order-no span span:nth-child(1)")
|
||||||
number = header.text
|
number = header.text
|
||||||
|
|
||||||
if number not in modified:
|
if number not in modified:
|
||||||
# 选择商机
|
# 选择商机
|
||||||
|
for attempt in range(args.retry):
|
||||||
try:
|
try:
|
||||||
selected = False
|
|
||||||
relation = lookup(number, '商机号', workbook).map(lambda x: x[0]).unwrap()
|
relation = lookup(number, '商机号', workbook).map(lambda x: x[0]).unwrap()
|
||||||
|
match = re.match(r'O\d+', str(relation))
|
||||||
|
|
||||||
if re.match(r'O\d+', str(relation)) is not None:
|
if match is not None:
|
||||||
try:
|
try:
|
||||||
href = locate(".layout-sidebar ul li.list-none.opportunity a").get_attribute('href')
|
href = locate(".layout-sidebar ul li.list-none.opportunity a").get_attribute('href')
|
||||||
driver.switch_to.new_window('tab')
|
driver.switch_to.new_window('tab')
|
||||||
@@ -400,14 +385,19 @@ def main(workbook=None):
|
|||||||
for item in menu.find_elements(By.CSS_SELECTOR, "ul li span"):
|
for item in menu.find_elements(By.CSS_SELECTOR, "ul li span"):
|
||||||
driver.execute_script("arguments[0].scrollIntoView({ block: 'center' });", item)
|
driver.execute_script("arguments[0].scrollIntoView({ block: 'center' });", item)
|
||||||
if item.text.startswith(relation):
|
if item.text.startswith(relation):
|
||||||
item.click()
|
click(item)
|
||||||
selected = True
|
associative = True
|
||||||
|
raise KeyboardInterrupt()
|
||||||
|
except KeyboardInterrupt:
|
||||||
break
|
break
|
||||||
if not selected:
|
|
||||||
raise Exception(f'无法找到对应商机 "{relation}"')
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[警告] {number}: 关联商机时发生错误:{e}")
|
print(f"[警告] {number}: 关联商机时发生错误:{e}")
|
||||||
warn = True
|
warn = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not associative:
|
||||||
|
print(f'[警告] {number}: 无法找到对应商机 "{relation}"')
|
||||||
|
warn = True
|
||||||
|
|
||||||
# 设定分页选项 10 条/页
|
# 设定分页选项 10 条/页
|
||||||
try:
|
try:
|
||||||
@@ -429,11 +419,9 @@ def main(workbook=None):
|
|||||||
|
|
||||||
while index < 10:
|
while index < 10:
|
||||||
if len(ids) >= len(positions): raise KeyboardInterrupt()
|
if len(ids) >= len(positions): raise KeyboardInterrupt()
|
||||||
# 获取项
|
|
||||||
if index >= 5: driver.execute_script("arguments[0].scrollTo(0, arguments[0].scrollHeight);", wrapper)
|
if index >= 5: driver.execute_script("arguments[0].scrollTo(0, arguments[0].scrollHeight);", wrapper)
|
||||||
items = wrapper.find_elements(By.CSS_SELECTOR, ".row-item")
|
|
||||||
|
|
||||||
for item in items:
|
for item in wrapper.find_elements(By.CSS_SELECTOR, ".row-item"):
|
||||||
# 订单序号
|
# 订单序号
|
||||||
try: serial = item.find_element(By.CSS_SELECTOR, ".cell[data-cci='2'] .cell-inner div").text
|
try: serial = item.find_element(By.CSS_SELECTOR, ".cell[data-cci='2'] .cell-inner div").text
|
||||||
except: continue
|
except: continue
|
||||||
@@ -491,8 +479,8 @@ def main(workbook=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
button = locate(".okki-pagination-next button", wait=False)
|
button = locate(".okki-pagination-next button", wait=False)
|
||||||
if bool(button.get_attribute('disabled')): raise Exception()
|
if bool(button.get_attribute('disabled')): raise KeyboardInterrupt()
|
||||||
driver.execute_script("arguments[0].click()", button)
|
click(button)
|
||||||
except:
|
except:
|
||||||
print('[信息] 已经是最后一页')
|
print('[信息] 已经是最后一页')
|
||||||
break
|
break
|
||||||
@@ -500,13 +488,13 @@ def main(workbook=None):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
class FieldArray[K, V]:
|
class FieldArray[K, V]:
|
||||||
def __init__(self, *args: K):
|
def __init__(self):
|
||||||
self.map: dict[K, list[V]] = { name: [] for name in args }
|
self.map: dict[K, list[V]] = {}
|
||||||
self.index = 0
|
self.index = 0
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def append(self, key: K, value: V):
|
def append(self, key: K, value: V):
|
||||||
array = self.map.get(key)
|
array = self.map.setdefault(key, [])
|
||||||
array.insert(self.index, value)
|
array.insert(self.index, value)
|
||||||
|
|
||||||
def newrow(self, padding=None):
|
def newrow(self, padding=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user