diff --git a/备货.ipynb b/备货.ipynb new file mode 100644 index 0000000..f63b762 --- /dev/null +++ b/备货.ipynb @@ -0,0 +1,310 @@ +{ + "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 +}