测试版终版
This commit is contained in:
75
new_页面内容.py
75
new_页面内容.py
@ -6,7 +6,7 @@ import re
|
||||
import urllib.parse
|
||||
import webbrowser
|
||||
import json
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog, messagebox
|
||||
|
||||
@ -14,18 +14,23 @@ import requests
|
||||
import pandas as pd
|
||||
from lxml import html
|
||||
|
||||
# ================= 1. 导入 UI 库 =================
|
||||
# ================= 1. 导入 UI 库 (已修正路径) =================
|
||||
import ttkbootstrap as ttk
|
||||
from ttkbootstrap.constants import *
|
||||
from ttkbootstrap.dialogs import Messagebox
|
||||
|
||||
# 兼容导入
|
||||
# 修正后的组件导入
|
||||
try:
|
||||
from ttkbootstrap.widgets import ScrolledText, Tableview, ToastNotification
|
||||
except ImportError:
|
||||
from ttkbootstrap.widgets import DateEntry
|
||||
from ttkbootstrap.scrolled import ScrolledText
|
||||
from ttkbootstrap.tableview import Tableview
|
||||
from ttkbootstrap.toast import ToastNotification
|
||||
except ImportError:
|
||||
# 兼容性导入
|
||||
from ttkbootstrap.widgets import DateEntry
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
from ttkbootstrap.tableview import Tableview
|
||||
from ttkbootstrap.toast import ToastNotification
|
||||
|
||||
|
||||
# ================= 2. 后端核心逻辑 =================
|
||||
@ -65,7 +70,6 @@ class CRMCrawler:
|
||||
return int(time.time() * 1000)
|
||||
|
||||
def clean_num(self, val):
|
||||
"""将 1.0000 转换为 1,保留必要的小数,为空则返回空字符串"""
|
||||
if val is None or val == "": return ""
|
||||
try:
|
||||
f_val = float(val)
|
||||
@ -83,12 +87,6 @@ class CRMCrawler:
|
||||
return 0.0
|
||||
|
||||
def fetch_product_details(self, record_id, contract_no, sales_person, outsourced_desc_from_html):
|
||||
"""
|
||||
修正后的价格逻辑:
|
||||
1. 销售单价/销售总价 -> 永远不填 (代码中置空)
|
||||
2. 厂家是"外购" -> 报价单价/总价置空,总金额填入"外购"列
|
||||
3. 厂家非"外购" -> 金额填入报价单价/总价,"外购"列置空
|
||||
"""
|
||||
detail_payload = {
|
||||
"module": "Plugins", "pluginName": "DetailProductTable", "action": "getTableData",
|
||||
"moduleName": "SalesOrder", "record": record_id, "actionId": self.get_timestamp(), "isTool": "1"
|
||||
@ -114,7 +112,6 @@ class CRMCrawler:
|
||||
products.append(v)
|
||||
|
||||
for prod in products:
|
||||
# 1. 基础信息
|
||||
manufacturer = self._get_nested_val(prod, 'cf_2128') or self._get_nested_val(prod, 'manufacturer')
|
||||
prod_desc_text = prod.get('productname', '')
|
||||
unit = self._get_nested_val(prod, 'usageunit')
|
||||
@ -122,23 +119,19 @@ class CRMCrawler:
|
||||
discount = self.clean_num(self._get_nested_val(prod, 'discount_percent'))
|
||||
currency = self._get_nested_val(prod, 'cf_534')
|
||||
|
||||
# 2. 价格获取
|
||||
list_price_raw = self._get_nested_val(prod, 'listPrice')
|
||||
f_qty = self._safe_float(qty_raw)
|
||||
f_list_price = self._safe_float(list_price_raw)
|
||||
f_total_val = f_list_price * f_qty # 计算总价
|
||||
f_total_val = f_list_price * f_qty
|
||||
|
||||
# 3. 判断外购逻辑
|
||||
is_outsourced = False
|
||||
if manufacturer and "外购" in manufacturer:
|
||||
is_outsourced = True
|
||||
|
||||
# 4. 处理产品描述
|
||||
final_desc = prod_desc_text
|
||||
if is_outsourced and outsourced_desc_from_html:
|
||||
final_desc = outsourced_desc_from_html
|
||||
|
||||
# 5. 分配金额到指定列
|
||||
col_quote_unit = ""
|
||||
col_quote_total = ""
|
||||
col_sales_unit = ""
|
||||
@ -146,14 +139,11 @@ class CRMCrawler:
|
||||
col_outsourced = ""
|
||||
|
||||
if is_outsourced:
|
||||
# 外购:报价列为空,金额填入外购列(总价)
|
||||
col_outsourced = self.clean_num(f_total_val)
|
||||
else:
|
||||
# 非外购:金额填入报价列,外购列为空
|
||||
col_quote_unit = self.clean_num(f_list_price)
|
||||
col_quote_total = self.clean_num(f_total_val)
|
||||
|
||||
# 构建行数据
|
||||
row = {
|
||||
"合同编号": contract_no,
|
||||
"销售员": sales_person,
|
||||
@ -165,11 +155,10 @@ class CRMCrawler:
|
||||
"币种": currency,
|
||||
"报价单价": col_quote_unit,
|
||||
"报价总价": col_quote_total,
|
||||
"销售单价": col_sales_unit, # 留空
|
||||
"销售总价": col_sales_total, # 留空
|
||||
"销售单价": col_sales_unit,
|
||||
"销售总价": col_sales_total,
|
||||
"折扣率": discount,
|
||||
"外购": col_outsourced,
|
||||
# 预留列
|
||||
"合同币种/美元": "",
|
||||
"外购转美元": "",
|
||||
"报价总价美元": "",
|
||||
@ -429,15 +418,18 @@ class CRMGUI(ttk.Window):
|
||||
self.nb_mode = ttk.Notebook(mode_grp, bootstyle="primary")
|
||||
self.nb_mode.pack(fill=BOTH, expand=True)
|
||||
|
||||
# === 📅 日期选择部分 ===
|
||||
f_date = ttk.Frame(self.nb_mode, padding=10)
|
||||
self.nb_mode.add(f_date, text="📅 按时间范围")
|
||||
self.ent_start = ttk.Entry(f_date, width=12);
|
||||
self.ent_start.insert(0, "2026-01-14");
|
||||
|
||||
self.ent_start = DateEntry(f_date, dateformat='%Y-%m-%d', width=11, bootstyle="primary")
|
||||
self.ent_start.pack(side=LEFT, padx=5)
|
||||
|
||||
ttk.Label(f_date, text="至").pack(side=LEFT)
|
||||
self.ent_end = ttk.Entry(f_date, width=12);
|
||||
self.ent_end.insert(0, "2026-01-15");
|
||||
|
||||
self.ent_end = DateEntry(f_date, dateformat='%Y-%m-%d', width=11, bootstyle="primary")
|
||||
self.ent_end.pack(side=LEFT, padx=5)
|
||||
# =========================
|
||||
|
||||
f_search = ttk.Frame(self.nb_mode, padding=10)
|
||||
self.nb_mode.add(f_search, text="🔍 关键词搜索")
|
||||
@ -527,7 +519,6 @@ class CRMGUI(ttk.Window):
|
||||
tv.pack(side=LEFT, fill=BOTH, expand=True)
|
||||
|
||||
for c in cols:
|
||||
# === 全居中设置 ===
|
||||
tv.heading(c, text=c, anchor="center")
|
||||
w = 100
|
||||
if "描述" in c or "标的" in c or "公司" in c or "单位" in c:
|
||||
@ -540,7 +531,6 @@ class CRMGUI(ttk.Window):
|
||||
w = 80
|
||||
tv.column(c, width=w, minwidth=50, anchor="center")
|
||||
|
||||
# 移除双击编辑,保留右键菜单(仅用于浏览器打开)
|
||||
tv.bind("<Button-3>", lambda e: self.on_right_click(e, tv, key))
|
||||
|
||||
self.treeviews[key] = tv
|
||||
@ -587,13 +577,14 @@ class CRMGUI(ttk.Window):
|
||||
kwargs = {}
|
||||
if curr_idx == 0:
|
||||
mode = "date"
|
||||
kwargs = {'start': self.ent_start.get(), 'end': self.ent_end.get()}
|
||||
kwargs = {'start': self.ent_start.entry.get(), 'end': self.ent_end.entry.get()}
|
||||
elif curr_idx == 1:
|
||||
mode = "search"
|
||||
kwargs = {'query': self.ent_query.get()}
|
||||
|
||||
try:
|
||||
self.crawler.run_task(mode, **kwargs); self.log_msg("🎉 完成!")
|
||||
self.crawler.run_task(mode, **kwargs);
|
||||
self.log_msg("🎉 完成!")
|
||||
except Exception as e:
|
||||
self.log_msg(f"❌ 错误: {e}")
|
||||
finally:
|
||||
@ -626,7 +617,6 @@ class CRMGUI(ttk.Window):
|
||||
self.stored_data[main_key][sub_key].append(record)
|
||||
record_idx = len(self.stored_data[main_key][sub_key]) - 1
|
||||
|
||||
# 主表
|
||||
tv_key = f"{main_key}_{sub_key}"
|
||||
tv = self.treeviews.get(tv_key)
|
||||
if tv:
|
||||
@ -634,7 +624,6 @@ class CRMGUI(ttk.Window):
|
||||
vals = [record.get(c, "") for c in cols]
|
||||
tv.insert("", END, iid=f"main_{main_key}_{sub_key}_{record_idx}", values=vals)
|
||||
|
||||
# 明细表
|
||||
detail_key_suffix = ""
|
||||
if sub_key == "Domestic":
|
||||
detail_key_suffix = "Domestic"
|
||||
@ -659,7 +648,6 @@ class CRMGUI(ttk.Window):
|
||||
if not item_id: return
|
||||
tv.selection_set(item_id)
|
||||
|
||||
# 仅在主表行点击时提供浏览器打开功能
|
||||
if item_id.startswith("main_"):
|
||||
parts = item_id.split('_')
|
||||
main_key, sub_key, idx = parts[1], parts[2], int(parts[3])
|
||||
@ -675,7 +663,6 @@ class CRMGUI(ttk.Window):
|
||||
url = f"http://111.198.24.44:88/index.php?module=SalesOrder&action=DetailView&record={crm_id}"
|
||||
webbrowser.open(url)
|
||||
|
||||
# --- 导出功能 ---
|
||||
def export_data(self):
|
||||
folder = filedialog.askdirectory()
|
||||
if not folder: return
|
||||
@ -709,6 +696,11 @@ class CRMGUI(ttk.Window):
|
||||
else:
|
||||
detail_domestic_rows.extend(products)
|
||||
|
||||
# ========== 核心修改:按合同编号升序排列 ==========
|
||||
detail_domestic_rows.sort(key=lambda x: x.get("合同编号", ""))
|
||||
detail_foreign_rows.sort(key=lambda x: x.get("合同编号", ""))
|
||||
# ===============================================
|
||||
|
||||
path = os.path.join(folder, f"{prefix}_{ts}.xlsx")
|
||||
try:
|
||||
with pd.ExcelWriter(path, engine='openpyxl') as writer:
|
||||
@ -718,6 +710,9 @@ class CRMGUI(ttk.Window):
|
||||
if c not in df.columns: df[c] = ""
|
||||
cols = export_cols[:2] + ["内贸合同号"] + export_cols[2:]
|
||||
df = df.reindex(columns=cols)
|
||||
|
||||
# --- 排序 ---
|
||||
df.sort_values(by="合同编号", ascending=True, inplace=True)
|
||||
df.to_excel(writer, sheet_name='内贸汇总', index=False)
|
||||
|
||||
if data_map['Foreign']:
|
||||
@ -726,6 +721,9 @@ class CRMGUI(ttk.Window):
|
||||
if c not in df.columns: df[c] = ""
|
||||
cols = export_cols[:2] + ["外贸合同号"] + export_cols[2:]
|
||||
df = df.reindex(columns=cols)
|
||||
|
||||
# --- 排序 ---
|
||||
df.sort_values(by="合同编号", ascending=True, inplace=True)
|
||||
df.to_excel(writer, sheet_name='外贸汇总', index=False)
|
||||
|
||||
if data_map['Other']:
|
||||
@ -734,16 +732,21 @@ class CRMGUI(ttk.Window):
|
||||
if c not in df.columns: df[c] = ""
|
||||
cols = export_cols[:2] + ["内贸合同号"] + export_cols[2:]
|
||||
df = df.reindex(columns=cols)
|
||||
|
||||
# --- 排序 ---
|
||||
df.sort_values(by="合同编号", ascending=True, inplace=True)
|
||||
df.to_excel(writer, sheet_name='其他汇总', index=False)
|
||||
|
||||
if detail_domestic_rows:
|
||||
df_d = pd.DataFrame(detail_domestic_rows)
|
||||
df_d = df_d.reindex(columns=detail_cols_order)
|
||||
# (已在前面 List 阶段排序)
|
||||
df_d.to_excel(writer, sheet_name='内贸明细', index=False)
|
||||
|
||||
if detail_foreign_rows:
|
||||
df_f = pd.DataFrame(detail_foreign_rows)
|
||||
df_f = df_f.reindex(columns=detail_cols_order)
|
||||
# (已在前面 List 阶段排序)
|
||||
df_f.to_excel(writer, sheet_name='外贸明细', index=False)
|
||||
|
||||
self.log_msg(f" ✅ 导出成功: {os.path.basename(path)}")
|
||||
|
||||
Reference in New Issue
Block a user