From 5705e674b500144e49a72024c1bce9eefa75eacb Mon Sep 17 00:00:00 2001 From: yumoqing Date: Tue, 7 Jul 2026 12:20:39 +0800 Subject: [PATCH] feat: add orgid to sub_resellers + get_distribution_chain - create_sub_reseller auto-creates organization record with orgid - get_distribution_chain walks from product_owner down to seller via sub_resellers --- supplychain/__pycache__/init.cpython-310.pyc | Bin 20289 -> 23346 bytes supplychain/init.py | 68 ++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/supplychain/__pycache__/init.cpython-310.pyc b/supplychain/__pycache__/init.cpython-310.pyc index 5dc0ef3ad6b1ba1b47b4ae367d28378aa4f82350..c09cad69a2ccb1a1d88e539ac278fd47d878f937 100644 GIT binary patch literal 23346 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 delta 7290 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 GIsOOe$s&UQ diff --git a/supplychain/init.py b/supplychain/init.py index 39939e4..a9a401e 100644 --- a/supplychain/init.py +++ b/supplychain/init.py @@ -312,7 +312,7 @@ async def delete_supply_contract_item(request, params_kw): # ============================================================ async def create_sub_reseller(request, params_kw): - """Create a new sub-reseller.""" + """Create a new sub-reseller with orgid for discount/accounting.""" env = ServerEnv() user_id = await env.get_user() resellerid = await env.get_userorgid() @@ -321,9 +321,25 @@ async def create_sub_reseller(request, params_kw): async with db.sqlorContext(dbname) as sor: sub_reseller_code = data.get("sub_reseller_code") or _generate_sub_reseller_code(resellerid) now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + # Create org entry for this sub_reseller + new_orgid = getID() + org_rec = { + "id": new_orgid, + "orgname": data.get("sub_reseller_name"), + "orgtype": "sub_reseller", + "status": "1", + "created_by": user_id, + "created_at": now, + "updated_at": now + } + async with db.sqlorContext("sage") as sor_sage: + await sor_sage.C("organization", org_rec) + rec = { "id": getID(), "resellerid": resellerid, + "orgid": new_orgid, "sub_reseller_code": sub_reseller_code, "sub_reseller_name": data.get("sub_reseller_name"), "contact_person": data.get("contact_person"), @@ -553,6 +569,54 @@ async def delete_sales_ledger(request, params_kw): return json.dumps({"status": "ok", "message": "删除成功"}) +# ============================================================ +# Distribution Chain Query +# ============================================================ + +async def get_distribution_chain(product_ownerid, seller_orgid): + """Get the distribution chain from product owner to seller. + + Chain is: product_owner → ... → sub_resellers recursively → seller + Returns list of orgids from product_owner down to seller (inclusive). + If seller_orgid == product_ownerid, returns [product_ownerid]. + """ + chain = [product_ownerid] + if seller_orgid == product_ownerid: + return chain + + env = ServerEnv() + dbname = env.get_module_dbname('supplychain') + db = DBPools() + config = getConfig() + db.databases = config.databases + + # Walk down the sub_reseller chain from product_owner to seller + async with db.sqlorContext(dbname) as sor: + current = product_ownerid + visited = {product_ownerid} + while current != seller_orgid: + sql = """SELECT orgid FROM sub_resellers + WHERE resellerid = ${resellerid}$ AND status = '1' + LIMIT 100""" + recs = await sor.sqlExe(sql, {'resellerid': current}) + found = False + for r in (recs or []): + orgid = r.get('orgid') if hasattr(r, 'get') else getattr(r, 'orgid', None) + if orgid and orgid == seller_orgid: + chain.append(orgid) + return chain + if orgid and orgid not in visited: + visited.add(orgid) + chain.append(orgid) + current = orgid + found = True + break + if not found: + # Can't reach seller from current, chain ends here + break + return chain + + # ============================================================ # Discount Calculation API (called by other modules during sales) # ============================================================ @@ -945,6 +1009,8 @@ def load_supplychain(): env.delete_product_supplier_mapping = delete_product_supplier_mapping # Calculation API env.calculate_sale_amounts = calculate_sale_amounts + # Distribution chain query + env.get_distribution_chain = get_distribution_chain # Attachment folder helper env.ensure_contract_folder = ensure_contract_folder env.ContractAttachMgr = ContractAttachMgr