dy-dashboard/import_20260908_local.py
2026-10-28 11:14:17 +08:00

198 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""2026-09-08 钉盘数据增量导入:日出库补录(09-04/05/06店铺+09-07旺店通) + 库存09-08"""
import csv, os, json, subprocess, pymysql, importlib.util, sys
from datetime import datetime
from collections import defaultdict
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
s = importlib.util.spec_from_file_location('config', 'config.py')
m = importlib.util.module_from_spec(s); s.loader.exec_module(m)
DB = m.DB_CONFIG
SPACE_ID = '28814565790'
OB_FOLDER = '3NwLYZXWynyBjRB4tZjwNxDvVkyEqBQm'
IV_FOLDER = 'NkDwLng8ZLGrXmrnFxYq3vRqVKMEvZBY'
TMP = 'tmp_csv/update_20260908'
os.makedirs(TMP, exist_ok=True)
def si(v):
try: return int(float(str(v or 0).replace('"','').replace('=','').strip()))
except: return 0
def sf(v):
try: return float(str(v or 0).replace('"','').replace('=','').strip())
except: return 0.0
def clean(v): return str(v or '').replace('"','').replace('=','').strip()
def bad(c):
n = clean(c).upper().rstrip(':')
return not n or n in ('NA','N/A','合计')
SPECIALS = {'POZ-FM-0184-TM-SM':.055,'POZ-XFS-9014-TM-MD':.062,'POZ-FM-9742-TM-MD':.181,
'POZ-MM-0580-TM-MD':.22,'POZ-SKS-3374-LD':.1,'POZ-SKS-3367-LD':.1,
'POZ-SKS-3350-LD':.1,'POZ-SKS-3343-LD':.1}
def dws_list(folder):
out=[]; cursor=None
while True:
args=['dws','drive','list','--space-id',SPACE_ID,'--folder',folder,'--limit','50','--format','json']
if cursor: args+=['--cursor',cursor]
r=subprocess.run(args,capture_output=True,text=True)
if r.returncode!=0:
print(' [list err]',r.stderr[:150]); break
d=json.loads(r.stdout).get('result',{})
out+=d.get('items',[])
cursor=d.get('nextToken')
if not cursor: break
return out
def dl(name, fileId):
dest=os.path.join(TMP,name)
if os.path.exists(dest) and os.path.getsize(dest)>10: return dest
r=subprocess.run(['dws','drive','download','--node',fileId,'--space-id',SPACE_ID,'--output',dest,'--format','json'],capture_output=True,text=True)
if r.returncode!=0 or not os.path.exists(dest) or os.path.getsize(dest)<10:
print(' [dl err]',name,r.stderr[:120]); return None
return dest
def read_rows_auto(path):
"""尝试多种编码,返回 (header_list, data_rows)"""
for enc in ['utf-8-sig','gbk','gb18030']:
try:
with open(path, encoding=enc) as f:
rr=list(csv.reader(f))
if rr: return rr[0], rr[1:]
except: continue
return None, None
def import_outbound(conn, date, src, path):
"""wdt 全格式 或 店铺2列简版;返回插入条数"""
with conn.cursor() as x:
x.execute('DELETE FROM daily_outbound WHERE 日期=%s AND 数据来源=%s',(date,src))
header, rows = read_rows_auto(path)
if rows is None:
print(' [read err]',path); return 0
inserted=0
with conn.cursor() as x:
if src == 'wdt':
# 全格式 DictReader(wdt 文件为 GBK)
with open(path, encoding='gb18030') as f:
for row in csv.DictReader(f):
code=clean(row.get('货品编号'))
if bad(code): continue
x.execute('INSERT INTO daily_outbound (货品编号,货品名称,品牌,仓库,店铺,一级类目,二级类目,三级类目,货品数量,货品成本,日期,数据来源,数据类型) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)',
(code,clean(row.get('货品名称')),clean(row.get('品牌')),clean(row.get('仓库')),clean(row.get('店铺')),
clean(row.get('一级类目')),clean(row.get('二级类目')),clean(row.get('三级类目')),
si(row.get('货品数量')),sf(row.get('货品成本')),date,src,'出库数据'))
inserted+=1
else:
# 店铺简版:跳过表头,col0=编码 col1=数量
for raw in rows:
if not raw or len(raw)<2: continue
code=clean(raw[0]); qty=si(raw[1])
if bad(code): continue
x.execute('SELECT product_name, brand, category1, category2, category3, cost_price FROM products WHERE product_code=%s LIMIT 1',(code,))
p=x.fetchone()
if p:
pname=p['product_name'] or ''
if clean(pname) in ('-','--'): continue
brand=p['brand'] or ''; c1=p['category1'] or ''; c2=p['category2'] or ''; c3=p['category3'] or ''
cost=float(p['cost_price']) if p['cost_price'] else 0
else:
pname=brand=c1=c2=c3=''; cost=0
x.execute('INSERT INTO daily_outbound (货品编号,货品名称,品牌,仓库,店铺,一级类目,二级类目,三级类目,货品数量,货品成本,日期,数据来源,数据类型) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)',
(code,pname,brand,'','',c1,c2,c3,qty,cost,date,src,'出库数据'))
inserted+=1
return inserted
def import_inventory(conn, date, path, wh):
with conn.cursor() as x:
x.execute('DELETE FROM inventory_detail WHERE data_date=%s AND warehouse_code=%s',(date,wh))
inserted=0
with conn.cursor() as x:
with open(path, encoding='utf-8-sig') as f:
for row in csv.DictReader(f):
code=clean(row.get('货品编号'))
if bad(code) or clean(row.get('货品名称')) in ('-','--'): continue
cost=SPECIALS.get(code, sf(row.get('成本价')))
ct=clean(row.get('单品创建时间')); dt=None
for fmt in ('%Y-%m-%d %H:%M:%S','%Y-%m-%d'):
try: dt=datetime.strptime(ct,fmt); break
except: pass
x.execute('INSERT INTO inventory_detail (data_date,source,warehouse_code,goods_code,product_code,product_name,brand,cost_price,available_qty,production_in_transit,purchase_in_transit,category1,category2,category3,created_time) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)',
(date,'wdt',wh,clean(row.get('商家编码')),code,clean(row.get('货品名称')),clean(row.get('品牌')),cost,
si(row.get('库存')),si(row.get('生产在途量')),si(row.get('采购在途')),
clean(row.get('一级类目')),clean(row.get('二级类目')),clean(row.get('三级类目')),dt))
inserted+=1
return inserted
def main():
print('扫描钉盘...')
ob = dws_list(OB_FOLDER)
iv = dws_list(IV_FOLDER)
# 组织文件
ob_files = {} # name -> fileId
for x in ob:
if x['type']=='FILE' and x['name'].endswith('.csv'):
ob_files[x['name']]=x['fileId']
iv_files = {}
for x in iv:
if x['type']=='FILE' and x['name'].endswith('.csv'):
iv_files[x['name']]=x['fileId']
# 现有出库组合
conn = pymysql.connect(**DB, cursorclass=pymysql.cursors.DictCursor)
existing_ob = set()
with conn.cursor() as x:
x.execute("SELECT 日期,数据来源 FROM daily_outbound")
for r in x.fetchall():
d = r['日期'].strftime('%Y-%m-%d') if hasattr(r['日期'],'strftime') else str(r['日期'])[:10]
existing_ob.add((d, r['数据来源']))
x.execute("SELECT DISTINCT data_date FROM inventory_detail")
existing_iv = set(str(r['data_date'])[:10] for r in x.fetchall())
# 目标:日出库 09-04 ~ 09-07 所有来源
need_ob = []
for name, fid in sorted(ob_files.items()):
p = name.split('-')
if len(p) < 3 or not p[0].isdigit(): continue
date = f'{p[0][:4]}-{p[0][4:6]}-{p[0][6:8]}'
if not (date >= '2026-09-04' and date <= '2026-09-07'): continue
src = p[1]
if (date, src) in existing_ob: continue
need_ob.append((name, fid, date, src))
# 目标:库存 09-08
need_iv = []
for name, fid in sorted(iv_files.items()):
p = name.split('-')
if not (p and p[0]=='20260908' and name.endswith('.csv')): continue
wh = name.rsplit('-',1)[1][:-4]
if '2026-09-08' in existing_iv and wh in [n.rsplit('-',1)[1][:-4] for n in iv_files if n.startswith('20260908') and '2026-09-08' in existing_iv]:
pass
need_iv.append((name, fid, '2026-09-08', wh))
print(f'需导入日出库 {len(need_ob)} 个文件, 库存 {len(need_iv)} 个文件')
# 下载并导入出库
ob_ins = 0
for name, fid, date, src in need_ob:
p = dl(name, fid)
if not p: continue
n = import_outbound(conn, date, src, p)
ob_ins += n
print(f' 出库 {name} ({date} {src}) 导入 {n} 条')
# 下载并导入库存 09-08
iv_ins = 0
for name, fid, date, wh in need_iv:
p = dl(name, fid)
if not p: continue
n = import_inventory(conn, date, p, wh)
iv_ins += n
print(f' 库存 {name} (仓库{wh}) 导入 {n} 条')
conn.commit()
conn.close()
print(f'\n完成: 出库新增 {ob_ins} 条, 库存新增 {iv_ins} 条')
if __name__ == '__main__':
main()