#!/usr/bin/env python3 import argparse import subprocess import sys def _sql_escape(value: str) -> str: return value.replace("'", "''") def build_sql( product_no: str, province: str, city: str, district: str, days: int, warehouse_transfer_fee: float, depreciation_rate: float, limit: int, ) -> str: pno = _sql_escape(product_no.strip()) prov = _sql_escape(province.strip()) city_v = _sql_escape((city or "").strip()) dist_v = _sql_escape((district or "").strip()) wh_fee = f"{warehouse_transfer_fee:.2f}" dep_rate = f"{depreciation_rate:.4f}" return f""" USE [JSDataMiddlePlatform]; SET NOCOUNT ON; DECLARE @product_no nvarchar(120) = N'{pno}'; DECLARE @province nvarchar(50) = N'{prov}'; DECLARE @city nvarchar(50) = N'{city_v}'; DECLARE @district nvarchar(50) = N'{dist_v}'; DECLARE @days int = {days}; DECLARE @limit int = {limit}; DECLARE @warehouse_transfer_fee decimal(18,2) = {wh_fee}; DECLARE @depreciation_rate decimal(10,4) = {dep_rate}; DECLARE @start_time datetime2 = DATEADD(day, -@days, GETDATE()); DECLARE @end_time datetime2 = GETDATE(); DECLARE @province_norm nvarchar(50) = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(@province, ''), '省', ''), '市', ''), '壮族自治区', ''), '回族自治区', ''), '维吾尔自治区', ''), '自治区', ''), '特别行政区', ''), '地区', ''), ' ', ''); DECLARE @city_norm nvarchar(50) = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(@city, ''), '省', ''), '市', ''), '壮族自治区', ''), '回族自治区', ''), '维吾尔自治区', ''), '自治区', ''), '特别行政区', ''), '地区', ''), ' ', ''); DECLARE @district_norm nvarchar(50) = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(@district, ''), '省', ''), '市', ''), '壮族自治区', ''), '回族自治区', ''), '维吾尔自治区', ''), '自治区', ''), '特别行政区', ''), '地区', ''), ' ', ''); IF OBJECT_ID('tempdb..#sku_pool') IS NOT NULL DROP TABLE #sku_pool; SELECT DISTINCT s.sku_id, s.name AS sku_name, s.other_7 AS product_no, s.i_id AS style_code, s.other_1 AS spu INTO #sku_pool FROM dbo.jst_sku AS s WITH (NOLOCK) WHERE ISNULL(NULLIF(s.sku_id, ''), '') <> '' AND ( NULLIF(s.other_7, '') = @product_no OR NULLIF(s.i_id, '') = @product_no OR NULLIF(s.other_1, '') = @product_no OR NULLIF(s.sku_id, '') = @product_no OR NULLIF(s.other_7, '') LIKE @product_no + '%' ); INSERT INTO #sku_pool (sku_id, sku_name, product_no, style_code, spu) SELECT DISTINCT NULLIF(a.undefined10, '') AS sku_id, NULLIF(a.undefined36, '') AS sku_name, NULLIF(a.undefined96, '') AS product_no, NULL, NULL FROM dbo.jst_goods_profit_kpi_items_afs AS a WITH (NOLOCK) LEFT JOIN #sku_pool AS p ON p.sku_id = NULLIF(a.undefined10, '') WHERE p.sku_id IS NULL AND ISNULL(NULLIF(a.undefined10, ''), '') <> '' AND ( NULLIF(a.undefined96, '') = @product_no OR NULLIF(a.undefined96, '') LIKE @product_no + '%' ); IF OBJECT_ID('tempdb..#order_base') IS NOT NULL DROP TABLE #order_base; SELECT NULLIF(a.undefined2, '') AS order_id, NULLIF(a.undefined10, '') AS sku_id, COALESCE(NULLIF(s.other_7, ''), NULLIF(a.undefined96, ''), @product_no) AS product_no, ISNULL(NULLIF(a.undefined8, ''), 'unknown') AS shop_name, ISNULL(NULLIF(a.undefined73, ''), ISNULL(NULLIF(e.logistic_company_name, ''), '未标注')) AS express_company, COALESCE(NULLIF(a.undefined66, ''), NULLIF(s.other_3, ''), '未标注') AS ship_warehouse_name, ISNULL(NULLIF(a.undefined74, ''), '') AS province_raw, ISNULL(NULLIF(a.undefined75, ''), '') AS city_raw, ISNULL(NULLIF(e.receiver_address, ''), '') AS receiver_address, REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(NULLIF(a.undefined74, ''), ''), '省', ''), '市', ''), '壮族自治区', ''), '回族自治区', ''), '维吾尔自治区', ''), '自治区', ''), '特别行政区', ''), '地区', ''), ' ', '') AS province_norm, REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(NULLIF(a.undefined75, ''), ''), '省', ''), '市', ''), '壮族自治区', ''), '回族自治区', ''), '维吾尔自治区', ''), '自治区', ''), '特别行政区', ''), '地区', ''), ' ', '') AS city_norm, REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ISNULL(NULLIF(e.receiver_address, ''), ''), '省', ''), '市', ''), '壮族自治区', ''), '回族自治区', ''), '维吾尔自治区', ''), '自治区', ''), '特别行政区', ''), '地区', ''), ' ', '') AS receiver_address_norm, COALESCE( TRY_CONVERT(datetime2, NULLIF(a.undefined6, ''), 120), TRY_CONVERT(datetime2, REPLACE(NULLIF(a.undefined6, ''), '/', '-'), 120), TRY_CONVERT(datetime2, NULLIF(a.undefined6, ''), 111), TRY_CONVERT(datetime2, NULLIF(e.send_time, ''), 120), TRY_CONVERT(datetime2, a.createdAt) ) AS ship_dt, COALESCE( TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined29, '')), TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined26, '')), TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined21, '')), CAST(0 AS decimal(18, 2)) ) AS sale_amount, COALESCE( TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined31, '')), TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined22, '')), CAST(0 AS decimal(18, 2)) ) AS cost_amount, COALESCE( TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined30, '')), TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined20, '')), CAST(0 AS decimal(18, 2)) ) AS shipped_qty, COALESCE( TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined40, '')), CAST(0 AS decimal(18, 2)) ) + COALESCE( TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined49, '')), CAST(0 AS decimal(18, 2)) ) AS return_qty, COALESCE( TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined90, '')), CAST(0 AS decimal(18, 2)) ) AS return_freight_fee, COALESCE( TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined79, '')), CAST(0 AS decimal(18, 2)) ) AS warehouse_transfer_fee_obs INTO #order_base FROM dbo.jst_goods_profit_kpi_items_afs AS a WITH (NOLOCK) INNER JOIN #sku_pool AS p ON p.sku_id = NULLIF(a.undefined10, '') LEFT JOIN dbo.jst_sku AS s WITH (NOLOCK) ON s.sku_id = NULLIF(a.undefined10, '') LEFT JOIN dbo.jst_order_express_details AS e WITH (NOLOCK) ON NULLIF(e.so_id, '') = NULLIF(a.undefined2, '') WHERE ISNULL(NULLIF(a.undefined2, ''), '') <> '' AND ISNULL(NULLIF(a.undefined62, ''), '') NOT LIKE '%配件%' AND ISNULL(NULLIF(a.undefined62, ''), '') NOT LIKE '%特殊单%' AND a.createdAt >= DATEADD(day, -(@days + 7), GETDATE()); IF OBJECT_ID('tempdb..#region_line') IS NOT NULL DROP TABLE #region_line; SELECT ob.* INTO #region_line FROM #order_base AS ob WHERE ob.ship_dt >= @start_time AND ob.ship_dt < @end_time AND ( (@province_norm = '' AND ISNULL(NULLIF(ob.province_norm, ''), '') <> '') OR ob.province_norm LIKE '%' + @province_norm + '%' OR @province_norm LIKE '%' + ob.province_norm + '%' OR ob.receiver_address_norm LIKE '%' + @province_norm + '%' ) AND ( @city_norm = '' OR ob.city_norm LIKE '%' + @city_norm + '%' OR @city_norm LIKE '%' + ob.city_norm + '%' OR ob.receiver_address_norm LIKE '%' + @city_norm + '%' ) AND ( @district_norm = '' OR ob.receiver_address LIKE '%' + @district + '%' OR ob.receiver_address_norm LIKE '%' + @district_norm + '%' ); IF OBJECT_ID('tempdb..#order_agg') IS NOT NULL DROP TABLE #order_agg; SELECT rl.order_id, MAX(rl.shop_name) AS shop_name, MAX(rl.product_no) AS product_no, MAX(rl.ship_dt) AS ship_dt, SUM(ISNULL(rl.sale_amount, 0)) AS sale_amount_order, SUM(ISNULL(rl.cost_amount, 0)) AS cost_amount_order, SUM(ISNULL(rl.shipped_qty, 0)) AS shipped_qty_order, SUM(ISNULL(rl.return_qty, 0)) AS return_qty_order, SUM(ISNULL(rl.return_freight_fee, 0)) AS return_freight_fee_order, SUM(ISNULL(rl.warehouse_transfer_fee_obs, 0)) AS warehouse_transfer_fee_order INTO #order_agg FROM #region_line AS rl GROUP BY rl.order_id; IF OBJECT_ID('tempdb..#freight_order') IS NOT NULL DROP TABLE #freight_order; SELECT NULLIF(f.so_id, '') AS order_id, SUM(COALESCE(TRY_CONVERT(decimal(18, 2), f.freightCost), 0)) AS outbound_freight_order INTO #freight_order FROM dbo.fys_jstFreightCostDetail AS f WITH (NOLOCK) INNER JOIN #region_line AS rl ON rl.order_id = NULLIF(f.so_id, '') AND rl.sku_id = NULLIF(f.sku_id, '') WHERE f.send_date >= @start_time AND f.send_date < @end_time GROUP BY NULLIF(f.so_id, ''); IF OBJECT_ID('tempdb..#order_fact') IS NOT NULL DROP TABLE #order_fact; SELECT oa.order_id, oa.shop_name, oa.product_no, oa.ship_dt, oa.sale_amount_order, oa.cost_amount_order, oa.shipped_qty_order, oa.return_qty_order, oa.return_freight_fee_order, oa.warehouse_transfer_fee_order, COALESCE(fo.outbound_freight_order, 0) AS outbound_freight_order INTO #order_fact FROM #order_agg AS oa LEFT JOIN #freight_order AS fo ON fo.order_id = oa.order_id; IF OBJECT_ID('tempdb..#kpi') IS NOT NULL DROP TABLE #kpi; SELECT COUNT(*) AS sample_order_count, SUM(ISNULL(ofa.shipped_qty_order, 0)) AS shipped_qty_sum, SUM(ISNULL(ofa.return_qty_order, 0)) AS return_qty_sum, SUM(ISNULL(ofa.sale_amount_order, 0)) AS sale_amount_sum, SUM(ISNULL(ofa.cost_amount_order, 0)) AS cost_amount_sum, AVG(NULLIF(ISNULL(ofa.outbound_freight_order, 0), 0)) AS avg_outbound_freight_nonzero, AVG(ISNULL(ofa.outbound_freight_order, 0)) AS avg_outbound_freight_per_order, AVG(NULLIF(ISNULL(ofa.return_freight_fee_order, 0), 0)) AS avg_return_freight_observed, AVG(NULLIF(ISNULL(ofa.warehouse_transfer_fee_order, 0), 0)) AS avg_warehouse_transfer_observed, MIN(ofa.ship_dt) AS first_ship_time, MAX(ofa.ship_dt) AS last_ship_time INTO #kpi FROM #order_fact AS ofa; -- 1) 样本概览 SELECT @product_no AS input_product_no, @province AS input_province, @city AS input_city, @district AS input_district, @days AS lookback_days, (SELECT COUNT(*) FROM #sku_pool) AS matched_sku_count, k.sample_order_count, k.shipped_qty_sum, k.return_qty_sum, CASE WHEN ISNULL(k.shipped_qty_sum, 0) = 0 THEN NULL ELSE CAST(k.return_qty_sum / NULLIF(k.shipped_qty_sum, 0) AS decimal(10,4)) END AS return_rate, k.first_ship_time, k.last_ship_time FROM #kpi AS k; -- 2) 利润估算因子(按订单均值) SELECT CAST(CASE WHEN k.sample_order_count = 0 THEN NULL ELSE k.sale_amount_sum / NULLIF(k.sample_order_count, 0) END AS decimal(18,2)) AS avg_sale_amount_per_order, CAST(CASE WHEN k.sample_order_count = 0 THEN NULL ELSE k.cost_amount_sum / NULLIF(k.sample_order_count, 0) END AS decimal(18,2)) AS avg_cost_amount_per_order, CAST(ISNULL(k.avg_outbound_freight_per_order, 0) AS decimal(18,2)) AS est_outbound_freight_per_order_15d, CAST( COALESCE( k.avg_return_freight_observed, ISNULL(k.avg_outbound_freight_per_order, 0) * CASE WHEN ISNULL(k.shipped_qty_sum, 0) = 0 THEN 0 ELSE k.return_qty_sum / NULLIF(k.shipped_qty_sum, 0) END, 0 ) AS decimal(18,2) ) AS est_return_freight_per_order_15d, CAST(COALESCE(k.avg_warehouse_transfer_observed, @warehouse_transfer_fee, 0) AS decimal(18,2)) AS warehouse_transfer_fee_used, CAST( CASE WHEN k.sample_order_count = 0 THEN NULL ELSE (k.sale_amount_sum / NULLIF(k.sample_order_count, 0)) - (k.cost_amount_sum / NULLIF(k.sample_order_count, 0)) - ISNULL(k.avg_outbound_freight_per_order, 0) - COALESCE( k.avg_return_freight_observed, ISNULL(k.avg_outbound_freight_per_order, 0) * CASE WHEN ISNULL(k.shipped_qty_sum, 0) = 0 THEN 0 ELSE k.return_qty_sum / NULLIF(k.shipped_qty_sum, 0) END, 0 ) - COALESCE(k.avg_warehouse_transfer_observed, @warehouse_transfer_fee, 0) END AS decimal(18,2) ) AS est_profit_per_order, CAST( CASE WHEN k.sample_order_count = 0 THEN NULL WHEN (k.sale_amount_sum / NULLIF(k.sample_order_count, 0)) = 0 THEN NULL ELSE ( ( (k.sale_amount_sum / NULLIF(k.sample_order_count, 0)) - (k.cost_amount_sum / NULLIF(k.sample_order_count, 0)) - ISNULL(k.avg_outbound_freight_per_order, 0) - COALESCE( k.avg_return_freight_observed, ISNULL(k.avg_outbound_freight_per_order, 0) * CASE WHEN ISNULL(k.shipped_qty_sum, 0) = 0 THEN 0 ELSE k.return_qty_sum / NULLIF(k.shipped_qty_sum, 0) END, 0 ) - COALESCE(k.avg_warehouse_transfer_observed, @warehouse_transfer_fee, 0) ) / NULLIF((k.sale_amount_sum / NULLIF(k.sample_order_count, 0)), 0) ) END AS decimal(10,4) ) AS est_profit_margin FROM #kpi AS k; -- 3) 处置建议(退货/补偿/放货,含退回折旧损失) WITH decision_base AS ( SELECT k.sample_order_count, CAST(CASE WHEN k.sample_order_count = 0 THEN NULL ELSE k.sale_amount_sum / NULLIF(k.sample_order_count, 0) END AS decimal(18,2)) AS avg_sale, CAST(CASE WHEN k.sample_order_count = 0 THEN NULL ELSE k.cost_amount_sum / NULLIF(k.sample_order_count, 0) END AS decimal(18,2)) AS avg_cost, CAST(ISNULL(k.avg_outbound_freight_per_order, 0) AS decimal(18,2)) AS avg_outbound, CAST(COALESCE( k.avg_return_freight_observed, ISNULL(k.avg_outbound_freight_per_order, 0) * CASE WHEN ISNULL(k.shipped_qty_sum, 0) = 0 THEN 0 ELSE k.return_qty_sum / NULLIF(k.shipped_qty_sum, 0) END, 0 ) AS decimal(18,2)) AS est_return_freight, CAST(COALESCE(k.avg_warehouse_transfer_observed, @warehouse_transfer_fee, 0) AS decimal(18,2)) AS warehouse_transfer FROM #kpi AS k ), calc AS ( SELECT db.*, CAST(ISNULL(db.avg_cost, 0) * @depreciation_rate AS decimal(18,2)) AS depreciation_loss_if_return, CAST((ISNULL(db.avg_outbound, 0) + ISNULL(db.est_return_freight, 0) + ISNULL(db.warehouse_transfer, 0) + (ISNULL(db.avg_cost, 0) * @depreciation_rate)) AS decimal(18,2)) AS expected_loss_if_return, CAST((ISNULL(db.avg_sale, 0) - ISNULL(db.avg_cost, 0) - ISNULL(db.avg_outbound, 0)) AS decimal(18,2)) AS keep_order_net_before_comp, CAST((0 - ISNULL(db.avg_outbound, 0) - ISNULL(db.est_return_freight, 0) - ISNULL(db.warehouse_transfer, 0) - (ISNULL(db.avg_cost, 0) * @depreciation_rate)) AS decimal(18,2)) AS option_return_net_profit, CAST((0 - ISNULL(db.avg_cost, 0) - ISNULL(db.avg_outbound, 0)) AS decimal(18,2)) AS option_release_net_profit FROM decision_base AS db ), range_calc AS ( SELECT c.*, CAST( CASE WHEN c.sample_order_count = 0 THEN 0 WHEN c.keep_order_net_before_comp <= 0 THEN 0 ELSE CASE WHEN (c.keep_order_net_before_comp * 0.70) < (c.expected_loss_if_return * 0.90) THEN (c.keep_order_net_before_comp * 0.70) ELSE (c.expected_loss_if_return * 0.90) END END AS decimal(18,2) ) AS comp_high_raw FROM calc AS c ) SELECT rc.sample_order_count, rc.keep_order_net_before_comp AS est_profit_per_order, CAST(@depreciation_rate AS decimal(10,4)) AS depreciation_rate_used, rc.depreciation_loss_if_return, rc.expected_loss_if_return, CAST( CASE WHEN rc.sample_order_count = 0 THEN 0 WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw * 0.50 END AS decimal(18,2) ) AS compensation_low, CAST( CASE WHEN rc.sample_order_count = 0 THEN 0 WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw END AS decimal(18,2) ) AS compensation_high, rc.option_return_net_profit, rc.option_release_net_profit, CAST( rc.keep_order_net_before_comp - (CASE WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw * 0.50 END) AS decimal(18,2) ) AS option_discount_net_profit_low, CAST( rc.keep_order_net_before_comp - (CASE WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw END) AS decimal(18,2) ) AS option_discount_net_profit_high, CASE WHEN rc.sample_order_count = 0 THEN N'样本不足' WHEN ( rc.keep_order_net_before_comp - (CASE WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw END) ) >= rc.option_return_net_profit AND ( rc.keep_order_net_before_comp - (CASE WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw END) ) >= rc.option_release_net_profit THEN N'优惠留客' WHEN rc.option_return_net_profit >= rc.option_release_net_profit THEN N'退回' ELSE N'放货' END AS recommended_option, CASE WHEN rc.sample_order_count = 0 THEN N'样本不足,暂不做退补决策(建议放宽省市区或延长到30天)。' WHEN ( rc.keep_order_net_before_comp - (CASE WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw END) ) >= rc.option_return_net_profit AND ( rc.keep_order_net_before_comp - (CASE WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw END) ) >= rc.option_release_net_profit THEN N'建议优先优惠留客:在建议补偿区间内,净收益优于退回和放货。' WHEN rc.option_return_net_profit >= rc.option_release_net_profit THEN N'建议优先退回:按当前折旧率和运费估算,退回净损失低于放货。' ELSE N'建议优先放货:退回综合损失(含20%折旧)高于放货损失。' END AS recommended_action, CASE WHEN rc.sample_order_count >= 30 THEN 'high' WHEN rc.sample_order_count >= 10 THEN 'medium' WHEN rc.sample_order_count > 0 THEN 'low' ELSE 'none' END AS confidence_level, CAST( CASE WHEN rc.sample_order_count = 0 THEN NULL ELSE rc.expected_loss_if_return - (CASE WHEN rc.comp_high_raw <= 0 THEN 0 ELSE rc.comp_high_raw * 0.75 END) END AS decimal(18,2) ) AS est_saving_vs_return FROM range_calc AS rc; -- 4) 快递/仓库策略(近15天样本) SELECT TOP (@limit) rl.express_company, COUNT(DISTINCT rl.order_id) AS order_count, SUM(ISNULL(rl.shipped_qty, 0)) AS shipped_qty_sum, CAST( SUM(ISNULL(rl.shipped_qty, 0)) / NULLIF((SELECT SUM(ISNULL(shipped_qty, 0)) FROM #region_line), 0) AS decimal(10,4) ) AS shipped_qty_ratio FROM #region_line AS rl GROUP BY rl.express_company ORDER BY shipped_qty_sum DESC, order_count DESC; SELECT TOP (@limit) rl.ship_warehouse_name, COUNT(DISTINCT rl.order_id) AS order_count, SUM(ISNULL(rl.shipped_qty, 0)) AS shipped_qty_sum, CAST( SUM(ISNULL(rl.shipped_qty, 0)) / NULLIF((SELECT SUM(ISNULL(shipped_qty, 0)) FROM #region_line), 0) AS decimal(10,4) ) AS shipped_qty_ratio FROM #region_line AS rl GROUP BY rl.ship_warehouse_name ORDER BY shipped_qty_sum DESC, order_count DESC; """ def main() -> int: parser = argparse.ArgumentParser( description="Estimate profit-based aftersale decision (退回/优惠/放货) by product_no + region, including return depreciation loss" ) parser.add_argument("--product-no", required=True, help="商品货号(product_no/style_code/spu/SKU)") parser.add_argument("--province", required=True, help="省份") parser.add_argument("--city", default="", help="城市(可选)") parser.add_argument("--district", default="", help="区县(可选)") parser.add_argument("--days", type=int, default=15, help="lookback window, default 15") parser.add_argument("--warehouse-transfer-fee", type=float, default=0.0, help="仓储转运费兜底值(当样本无观测值时使用)") parser.add_argument("--depreciation-rate", type=float, default=0.20, help="退回折旧损失率,默认0.20(20%%)") parser.add_argument("--limit", type=int, default=10, help="max rows for strategy result sets") args = parser.parse_args() product_no = args.product_no.strip() province = args.province.strip() if not product_no: print("--product-no is required", file=sys.stderr) return 2 if not province: print("--province is required", file=sys.stderr) return 2 days = max(7, min(args.days, 90)) limit = max(1, min(args.limit, 100)) depreciation_rate = max(0.0, min(args.depreciation_rate, 1.0)) sql = build_sql( product_no=product_no, province=province, city=args.city, district=args.district, days=days, warehouse_transfer_fee=args.warehouse_transfer_fee, depreciation_rate=depreciation_rate, limit=limit, ) cmd = ["company-db", "query", "--sql", sql] proc = subprocess.run(cmd, capture_output=True, text=True) if proc.returncode != 0: err = (proc.stderr or proc.stdout or "company-db failed").strip() print(err, file=sys.stderr) return proc.returncode print(proc.stdout.rstrip()) return 0 if __name__ == "__main__": raise SystemExit(main())