logistics/备货.ipynb

311 lines
13 KiB
Plaintext
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.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"一stock_date表示备货日期"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 取备货时间date符合方案的SKU\n",
"\"\"\"\n",
"1.订单上线时间三个月以上\n",
"2.前一个月订单数>=4或前两个月订单数>=5\n",
"数据提取方案:\n",
"1.设置变量stock_date为备货时间\n",
"2.计算出stock_date的两个月前的order_date2和一个月前的order_date1\n",
"3.查询order_date1至stock_date的订单数和order_date2至stock_date的订单数\n",
"4.筛选出order_date1至stock_date的订单数>=4或order_date2至stock_date的订单数>=5的SKU\n",
"5.计算上线时间和date的差值,筛选>3个月天的产品\n",
"\"\"\"\n",
"import pandas as pd\n",
"import numpy as np\n",
"from datetime import datetime\n",
"from dateutil.relativedelta import relativedelta\n",
"\n",
"stock_date = '2025-11-18'\n",
"stock_datetime = datetime.strptime(stock_date, '%Y-%m-%d')\n",
"\n",
"# 计算一个月前和两个月前\n",
"order_date1 = (stock_datetime - relativedelta(months=1)).strftime('%Y-%m-%d') # 一个月前订单时间\n",
"order_date2 = (stock_datetime - relativedelta(months=2)).strftime('%Y-%m-%d') # 两个月前订单时间\n",
"online_date = (stock_datetime - relativedelta(months=3)).strftime('%Y-%m-%d') # 上线时间\n",
"\n",
"after_date1 = (stock_datetime + relativedelta(months=1)).strftime('%Y-%m-%d') # 一个月后订单时间\n",
"after_date2 = (stock_datetime + relativedelta(months=2)).strftime('%Y-%m-%d') # 两个月后订单时间\n",
"after_date3 = (stock_datetime + relativedelta(months=3)).strftime('%Y-%m-%d') # 三个月后订单时间\n",
"after_date4 = (stock_datetime + relativedelta(months=4)).strftime('%Y-%m-%d') # 四个月后订单时间\n",
"\n",
"print(after_date1, after_date2, after_date3, after_date4, order_date1,order_date2,online_date)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"当前板块直接跑是计算美国区的备货情况如果需要看全球的请搜索delivery_country并把这行注释掉在sql里"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 读取订单数据\n",
"from utils.gtools import MySQLconnect\n",
"with MySQLconnect('ods') as db:\n",
" enginal = db.engine()\n",
" sql = f\"\"\"\n",
" # 取采购价\n",
" with \n",
" pur_cost AS (\n",
" SELECT\n",
" SUBSTRING_INDEX(opl.order_product_id, '_', 2) AS order_product_id,\n",
" SUM(actual_price) AS 采购成本,\n",
" SKU,\n",
" ROW_NUMBER() OVER (PARTITION BY SKU ORDER BY order_date DESC) as rn\n",
" FROM\n",
" dws.order_product_list opl\n",
" LEFT JOIN ods.warehouse_purchasing wp ON opl.order_product_id = wp.order_product_id\n",
" WHERE\n",
" NOT EXISTS (\n",
" SELECT 1 \n",
" FROM dws.log_order_reissue_detail AS r \n",
" WHERE left(r.order_product_id,15) = opl.order_id)\n",
" AND order_date >= '{order_date1}'\n",
" AND order_date < '{stock_datetime}'\n",
" AND opl.fun_audit NOT REGEXP \"等待\"\n",
" AND opl.site_name REGEXP \"Litfad\"\n",
" AND opl.product_audit REGEXP \"采购完成\"\n",
" AND opl.SKU IS NOT NULL and opl.SKU <>0\n",
" GROUP BY \n",
" SUBSTRING_INDEX(opl.order_product_id, '_', 2) \n",
"\n",
" ),\n",
" t1 AS (\n",
" SELECT\n",
" spu.`产品品类`,\n",
" spu.`产品分类`,\n",
" pc.采购成本,\n",
" opl.SKU,\n",
" COUNT(DISTINCT CASE WHEN DATE_FORMAT(opl.order_date, '%%Y-%%m-%%d') >= '{order_date2}' and DATE_FORMAT(opl.order_date, '%%Y-%%m-%%d') < '{order_date1}' THEN opl.order_id END) AS 前第二月订单数,\n",
" COUNT(DISTINCT CASE WHEN DATE_FORMAT(opl.order_date, '%%Y-%%m-%%d') >= '{order_date1}' and DATE_FORMAT(opl.order_date, '%%Y-%%m-%%d') < '{stock_datetime}' THEN opl.order_id END) AS 前一月订单数,\n",
" SUM(CASE WHEN (DATE_FORMAT(opl.order_date, '%%Y-%%m-%%d') >= '{stock_datetime}' AND DATE_FORMAT(opl.order_date, '%%Y-%%m') < '{after_date1}') \n",
" THEN opl.product_num ELSE 0 END) AS 后第一月产品数,\n",
" SUM(CASE WHEN (DATE_FORMAT(opl.order_date, '%%Y-%%m') >= '{after_date1}' AND DATE_FORMAT(opl.order_date, '%%Y-%%m-%%d') <'{after_date2}') \n",
" THEN opl.product_num ELSE 0 END) AS 后第两月产品数,\n",
" SUM(CASE WHEN (DATE_FORMAT(opl.order_date, '%%Y-%%m') >= '{after_date2}' AND DATE_FORMAT(opl.order_date, '%%Y-%%m') < '{after_date3}') \n",
" THEN opl.product_num ELSE 0 END) AS 后第三月产品数,\n",
" SUM(CASE WHEN DATE_FORMAT(opl.order_date, '%%Y-%%m') >= '{after_date3}' \n",
" THEN opl.product_num ELSE 0 END) AS 后第四月产品数,\n",
" DATE_FORMAT(sku.添加时间,'%%Y-%%m-%%d') AS 上线时间,\n",
" DATE_FORMAT(sku.更新时间,'%%Y-%%m-%%d') AS 更新时间\n",
" \n",
" FROM\n",
" dws.order_product_list opl\n",
" LEFT JOIN pur_cost pc ON opl.SKU = pc.SKU \n",
" LEFT JOIN ods.order_list ol ON opl.order_id =ol.order_id\n",
" LEFT JOIN ods.stg_bayshop_litfad_sku sku ON opl.SKU = sku.SKU\n",
" LEFT JOIN ods.stg_bayshop_litfad_spu spu ON sku.`产品PID` = spu.`产品PID` \n",
" WHERE\n",
" opl.order_date >= '{order_date2}'\n",
" AND DATE_FORMAT(opl.order_date, '%%Y-%%m') < '{after_date4}'\n",
" AND DATE_FORMAT(sku.添加时间,'%%Y-%%m-%%d') < '{online_date}'\n",
" AND opl.SKU IS NOT NULL and opl.SKU <>0\n",
" AND opl.order_product_id NOT REGEXP '^[^_]*_[^_]*_[^_]*$'\n",
" AND site_type REGEXP \"独立站\"\n",
" AND ol.fund_status NOT REGEXP \"等待\"\n",
" and sku.状态 REGEXP \"启用\"\n",
" AND spu.状态 REGEXP \"正常\"\n",
" AND 采购成本>0\n",
" AND rn = 1\n",
" and 产品品类 <> \"126 - Outdoor\"\n",
" and 产品品类 <>\"57 - Rugs\"\n",
" and 产品分类 <> \"151 - Peel & Stick Backsplash Tile\"\n",
" and 产品分类 <> \"138 - Bathroom Sinks\"\n",
" and 产品分类 <> \"184 - Toddler & Kids Chairs\"\n",
" and opl.delivery_country regexp \"United States\"\n",
" GROUP BY opl.SKU\n",
" )\n",
" \n",
" \n",
" SELECT \n",
" 产品品类,\n",
" 产品分类,\n",
" SKU,\n",
" 采购成本,\n",
" 前第二月订单数+前一月订单数 AS 前两月订单数,\n",
" 前一月订单数 AS 备货数,\n",
" 后第一月产品数,\n",
" 后第两月产品数,\n",
" 后第三月产品数,\n",
" 后第四月产品数,\n",
" (后第一月产品数+后第两月产品数+后第三月产品数+后第四月产品数) AS 后四月总产品数\n",
" FROM \n",
" t1\n",
" WHERE\n",
" 前一月订单数 >=4 OR (前一月订单数>=2 AND (前第二月订单数+前一月订单数)>=5)\n",
" ORDER BY 前一月订单数 DESC\n",
" \"\"\"\n",
" df = pd.read_sql(sql, enginal)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 取上诉SKU的体积\n",
"import pandas as pd\n",
"from utils.gtools import MySQLconnect\n",
"\n",
"sku_list = (\n",
" df['SKU']\n",
" .apply(pd.to_numeric, errors='coerce')\n",
" .dropna()\n",
" .astype(int)\n",
" .astype(str)\n",
" .drop_duplicates() # 加这一行\n",
" .tolist()\n",
")\n",
"\n",
"# 读取需要计算的包裹信息\n",
"with MySQLconnect('ods') as db:\n",
" enginal = db.engine()\n",
" quoted_spus = ','.join([f\"'{sku}'\" for sku in sku_list])\n",
" sql = f\"\"\" \n",
" WITH\n",
" t1 AS (\n",
" SELECT\n",
" opl.order_id,\n",
" SKU,\n",
" ol.order_price_dollar AS 订单售价,\n",
" sum(CASE WHEN opl.order_product_id REGEXP \"[0-9]{{15}}_[0-9]*$\"\n",
" THEN product_num END) AS product_num,\n",
" count(DISTINCT opl.SKU) AS 产品种类\n",
" FROM\n",
" dws.order_product_list opl\n",
" left join ods.order_list ol ON opl.order_id = ol.order_id\n",
" WHERE\n",
" NOT EXISTS (\n",
" SELECT 1 \n",
" FROM dws.log_order_reissue_detail AS r \n",
" WHERE left(r.order_product_id,15) = opl.order_id\n",
" \n",
" )\n",
" and opl.delivery_country regexp \"United States\"\n",
" AND SKU in ({quoted_spus})\n",
" AND SKU <> \"\" AND SKU IS NOT NULL AND SKU <> 0\n",
" GROUP BY order_id\n",
" ),\n",
" t2 AS (\n",
" SELECT \n",
" t1.SKU,\n",
" 订单售价,\n",
" round(SUM(b.weight/1000 ),0)AS 重量,\n",
" ROUND(SUM(b.length*b.width*b.hight/1000000),2) AS 体积,\n",
" COUNT(package) AS 包裹数\n",
" FROM\n",
" t1\n",
" LEFT JOIN order_express a ON t1.order_id = a.单号\n",
" JOIN package_vol_info b ON a.`包裹号` = b.package\n",
" WHERE\n",
" a.`包裹状态` = '客户签收'\n",
" AND b.hight > 0 \n",
" AND b.length > 0 \n",
" AND b.width > 0 \n",
" AND b.hight > 0 \n",
" AND b.weight > 0\n",
" AND t1.product_num = 1\n",
" AND t1.产品种类=1\n",
" GROUP BY order_id\n",
" )\n",
" SELECT\n",
" SKU,\n",
" AVG(订单售价) AS 订单平均售价,\n",
" AVG(重量) AS 平均重量,\n",
" AVG(体积) AS 平均体积\n",
" FROM t2\n",
" GROUP BY SKU\n",
" \n",
"\n",
" \"\"\"\n",
" package_df = pd.read_sql(sql, enginal)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"all_df = pd.merge(df, package_df, on='SKU', how='left')\n",
"all_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 处理每个条目的不同备货要求\n",
"# 66 - Furniture\n",
"fir_df = all_df[\n",
" ((all_df['产品品类'] == '66 - Furniture') & (all_df['前两月订单数'] >5))|\n",
" ((all_df['产品品类'] == '1 - Lighting' )& (all_df['前两月订单数'] >5))]\n",
"\n",
"fir_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"len(fir_df)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fir_df.to_clipboard(index = False)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "base",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}