This commit is contained in:
2026-04-27 21:58:00 +08:00
parent 3b535652f2
commit a6636733d9
4 changed files with 142 additions and 128 deletions
+94 -82
View File
@@ -23,6 +23,7 @@ from ._base import (
db_query, TSupply, TDemand, ExternalBaseConnection, BaseSource, BaseVoucher, InternalData, ExternalData,
)
from project_files._base import async_rate_limit
MERGE_ENTRIY_KEY = '_entries_'
@@ -896,6 +897,7 @@ class TplusMo(BaseVoucher):
# self.data = data or {}
@async_rate_limit()
async def create(
self,
_aps: ApsPayloadSponsor,
@@ -903,51 +905,55 @@ class TplusMo(BaseVoucher):
pydantic_model: Type[PydanticModel] = None,
remain_native_supplyno: bool = True
):
assert self._CONNECTION, "未获得连接对象,请先注册"
await self._CONNECTION.auth()
try:
assert self._CONNECTION, "未获得连接对象,请先注册"
await self._CONNECTION.auth()
endpoint = self._CREATE_ENDPOINT
supplyno = self.raw_data.get('supplyno')
demand_list = await _aps.get_demand_datalist(demandno=supplyno)
supplymo_detaildata = await _aps.get_supplymo_detaildata(supplyno=supplyno, get_next_mo=True, get_origin_so=True)
supplymo_detaildata['demand_list'] = demand_list
endpoint = self._CREATE_ENDPOINT
supplyno = self.raw_data.get('supplyno')
demand_list = await _aps.get_demand_datalist(demandno=supplyno)
supplymo_detaildata = await _aps.get_supplymo_detaildata(supplyno=supplyno, get_next_mo=True, get_origin_so=True)
supplymo_detaildata['demand_list'] = demand_list
pydantic_model = pydantic_model or self._PUSH_PYDANTIC_MODEL
# dto = InternalData(data=supplymo_detaildata).dump(pydantic_model=pydantic_model)
dto = pydantic_model(**supplymo_detaildata).model_dump(exclude_none=True)
pydantic_model = pydantic_model or self._PUSH_PYDANTIC_MODEL
# dto = InternalData(data=supplymo_detaildata).dump(pydantic_model=pydantic_model)
dto = pydantic_model(**supplymo_detaildata).model_dump(exclude_none=True)
if remain_native_supplyno:
dto['Code'] = supplyno
payload = {"dto": dto}
mo_create_response_json = await self._CONNECTION._post(endpoint=endpoint, data=payload)
if str(mo_create_response_json['code']) == '0': # 响应错误码为0MO 创建成功
response_data = mo_create_response_json['data']
tplus_mo_id = response_data['ID']
tplus_mo_code = supplyno if remain_native_supplyno else response_data['Code']
# 审批 MO ,要在领料申请前批准
_x_a = await self.approve(tplus_moid=tplus_mo_id)
# 查询推送成功的 MO 在 T+ 中的详情
tplus_mo_data = (await TplusMo.query(index_value=tplus_mo_id)).first()
# 从 T+ 中提取 MO 详情中的第一个详情记录的 ID 作为 _entryid
tplus_mo_entryid = tplus_mo_data['ManufactureOrderDetails'][0]['ID']
# 调用存储过程更改工单信息,❗一定放在最后一步,否则工单号变更太早,前面若有用原生供应号查询都会失败
_x_b = await _erp.mo_release_success(
native_plno=supplyno,
msg=mo_create_response_json['message'],
msg_from='T+',
mono=tplus_mo_code,
_id=tplus_mo_id,
_entryid=tplus_mo_entryid
)
else:
_x_c = await _erp.mo_release_failed(
native_plno=supplyno,
msg=mo_create_response_json['message'],
push_data=payload,
msg_from='T+'
)
return self
if remain_native_supplyno:
dto['Code'] = supplyno
payload = {"dto": dto}
mo_create_response_json = await self._CONNECTION._post(endpoint=endpoint, data=payload)
if str(mo_create_response_json['code']) == '0': # 响应错误码为0MO 创建成功
response_data = mo_create_response_json['data']
tplus_mo_id = response_data['ID']
tplus_mo_code = supplyno if remain_native_supplyno else response_data['Code']
# 审批 MO ,要在领料申请前批准
_x_a = await self.approve(tplus_moid=tplus_mo_id)
# 查询推送成功的 MO 在 T+ 中的详情
tplus_mo_data = (await TplusMo.query(index_value=tplus_mo_id)).first()
# 从 T+ 中提取 MO 详情中的第一个详情记录的 ID 作为 _entryid
tplus_mo_entryid = tplus_mo_data['ManufactureOrderDetails'][0]['ID']
# 调用存储过程更改工单信息,❗一定放在最后一步,否则工单号变更太早,前面若有用原生供应号查询都会失败
_x_b = await _erp.mo_release_success(
native_plno=supplyno,
msg=mo_create_response_json['message'],
msg_from='T+',
mono=tplus_mo_code,
_id=tplus_mo_id,
_entryid=tplus_mo_entryid
)
else:
_x_c = await _erp.mo_release_failed(
native_plno=supplyno,
msg=mo_create_response_json['message'],
push_data=payload,
msg_from='T+'
)
return self
except Exception as e:
logger.warning("创建生产加工单失败", str(e))
_erp.mo_release_failed(native_plno=supplyno, msg=str(e))
@classmethod
@@ -998,53 +1004,58 @@ class TplusRs(BaseVoucher):
super().__init__(supplymo_data)
@async_rate_limit()
async def create(self,
_aps: ApsPayloadSponsor,
_erp: EventResultPoster,
pydantic_model: Type[PydanticModel] = None
):
assert self._CONNECTION, "未获得连接对象,请先注册"
await self._CONNECTION.auth()
endpoint = self._CREATE_ENDPOINT
pydantic_model = pydantic_model or self._PUSH_PYDANTIC_MODEL
try:
assert self._CONNECTION, "未获得连接对象,请先注册"
await self._CONNECTION.auth()
endpoint = self._CREATE_ENDPOINT
pydantic_model = pydantic_model or self._PUSH_PYDANTIC_MODEL
rs_no = self.raw_data.get('supplyno')
tplus_mo_id = self.raw_data.get('apiex_id')
rs_data_list = await _aps.get_demand_datalist(demandno=rs_no)
tplus_mo_data = (await TplusMo.query(index_value=tplus_mo_id)).first()
rs_no = self.raw_data.get('supplyno')
tplus_mo_id = self.raw_data.get('apiex_id')
rs_data_list = await _aps.get_demand_datalist(demandno=rs_no)
tplus_mo_data = (await TplusMo.query(index_value=tplus_mo_id)).first()
processed_rsdata = DataProcessor.merge_common_fields(
data=rs_data_list,
merge_with=["demandno", "type", "status", "create_date"],
entries_key=MERGE_ENTRIY_KEY
)
processed_rsdata = DataProcessor.merge_common_fields(
data=rs_data_list,
merge_with=["demandno", "type", "status", "create_date"],
entries_key=MERGE_ENTRIY_KEY
)
mo_id = tplus_mo_data['ID']
# mo_code = tplus_mo_data['Code']
# mo_depart_code = tplus_mo_data.get('Department', {}).get('Code', '')
tplus_mo_entryid = tplus_mo_data['ManufactureOrderDetails'][0]['ID']
mo_material_details = tplus_mo_data['ManufactureOrderDetails'][0]['ManufactureOrderMaterialDetails']
# mo_material_details_id = mo_material_details[0]['ID']
mo_id = tplus_mo_data['ID']
# mo_code = tplus_mo_data['Code']
# mo_depart_code = tplus_mo_data.get('Department', {}).get('Code', '')
tplus_mo_entryid = tplus_mo_data['ManufactureOrderDetails'][0]['ID']
mo_material_details = tplus_mo_data['ManufactureOrderDetails'][0]['ManufactureOrderMaterialDetails']
# mo_material_details_id = mo_material_details[0]['ID']
processed_rsdata['tplus_mo_id'] = mo_id
processed_rsdata['tplus_mo_entryid'] = tplus_mo_entryid
processed_rsdata['tplus_mo_data'] = tplus_mo_data
# processed_rsdata['mo_material_details_id'] = mo_material_details_id
processed_rsdata['mo_material_details'] = mo_material_details
processed_rsdata['tplus_mo_id'] = mo_id
processed_rsdata['tplus_mo_entryid'] = tplus_mo_entryid
processed_rsdata['tplus_mo_data'] = tplus_mo_data
# processed_rsdata['mo_material_details_id'] = mo_material_details_id
processed_rsdata['mo_material_details'] = mo_material_details
dto = pydantic_model(**processed_rsdata).model_dump()
if dto["MaterialRequestDetails"]: # 有领料申请详情
payload = {"dto": dto}
logger.debug(f"向 T+ 推送领料申请,发送数据:{json.dumps(payload, ensure_ascii=False)}")
rs_push_response_json = await self._CONNECTION._post(endpoint=endpoint, data=payload)
dto = pydantic_model(**processed_rsdata).model_dump()
if dto["MaterialRequestDetails"]: # 有领料申请详情
payload = {"dto": dto}
logger.debug(f"向 T+ 推送领料申请,发送数据:{json.dumps(payload, ensure_ascii=False)}")
rs_push_response_json = await self._CONNECTION._post(endpoint=endpoint, data=payload)
if str(rs_push_response_json['code']) == '0': # 创建成功
await _erp.rs_release_success(rsno=rs_no, msg=rs_push_response_json['message'], msg_from='T+', _code=rs_push_response_json['data'].get('Code'), _id=rs_push_response_json['data'].get('ID'))
if str(rs_push_response_json['code']) == '0': # 创建成功
await _erp.rs_release_success(rsno=rs_no, msg=rs_push_response_json['message'], msg_from='T+', _code=rs_push_response_json['data'].get('Code'), _id=rs_push_response_json['data'].get('ID'))
else:
await _erp.rs_release_failed(rsno=rs_no, msg=rs_push_response_json['message'], push_data=processed_rsdata, msg_from='T+')
else:
await _erp.rs_release_failed(rsno=rs_no, msg=rs_push_response_json['message'], push_data=processed_rsdata, msg_from='T+')
else:
await _erp.rs_release_success(rsno=rs_no, msg="领料申请详情", msg_from='APS')
await _erp.rs_release_success(rsno=rs_no, msg="无领料申请详情", msg_from='APS')
except Exception as e:
logger.warning_msg(f"创建领料申请单失败", str(e))
await _erp.rs_release_failed(rsno=rs_no, msg=str(e))
@@ -1068,17 +1079,18 @@ class TplusPr(BaseVoucher):
self.raw_data = event_data_list
@async_rate_limit()
async def create(self,
# _aps: ApsPayloadSponsor,
_erp: EventResultPoster,
pydantic_model: Type[PydanticModel] = None
):
assert self._CONNECTION, "未获得连接对象,请先注册"
await self._CONNECTION.auth()
endpoint = self._CREATE_ENDPOINT
pydantic_model = pydantic_model or self._PUSH_PYDANTIC_MODEL
try:
assert self._CONNECTION, "未获得连接对象,请先注册"
await self._CONNECTION.auth()
endpoint = self._CREATE_ENDPOINT
pydantic_model = pydantic_model or self._PUSH_PYDANTIC_MODEL
# 转换为 T+ 格式
agg_data_list = await ApsPayloadSponsor.aggregate_pr_data(pr_data_list=self.raw_data)
tplus_pr_data = pydantic_model(data=agg_data_list).model_dump(exclude_none=True)
+23 -36
View File
@@ -186,62 +186,49 @@ planner_email_reminder = QqEmailReminder(
@event_batch_handler(reminder=planner_email_reminder)
async def batch_handle_pl_status_a2e(event_data_list: list[dict], _erp: EventResultPoster, description="下达生产加工单至 T+"):
@async_rate_limit()
async def handle_pl_status_a2e(event_data: dict, _aps: ApsPayloadSponsor):
"""处理PL状态变更为A2E"""
try:
await TplusMo(event_data).create(
_aps=_aps,
_erp=_erp,
pydantic_model=_CustomMoPushModel,
remain_native_supplyno=REMAIN_NATIVE_SUPPLYNO
)
except Exception as e:
CLIENT_LOGGER.fail("处理PL状态变更", str(event_data), str(e))
await _erp.mo_release_failed(supplyno, msg=str(e))
supply_nos = [s['supplyno'] for s in event_data_list]
await TSupply.filter(supplyno__in=supply_nos).update(memo=" 📤 正在推送至 T+ ...")
aps_payload_sponsor = ApsPayloadSponsor(production_cache_items=[CacheItem.SUPPLY_MO, CacheItem.DEMAND, CacheItem.MATERIAL])
await aps_payload_sponsor.establish_production_cache(supplynos=supply_nos)
_CustomMoPushModel = create_custom_mo_push_model(aps_payload_sponsor)
tasks = [handle_pl_status_a2e(event_data=item, _aps=aps_payload_sponsor) for item in event_data_list]
tasks = [
TplusMo(event_data).create(
_aps=aps_payload_sponsor,
_erp=_erp,
pydantic_model=_CustomMoPushModel,
remain_native_supplyno=REMAIN_NATIVE_SUPPLYNO
)
for event_data in event_data_list
]
await asyncio.gather(*tasks, return_exceptions=True)
@event_batch_handler(reminder=planner_email_reminder)
async def batch_handle_pl_to_mo(event_data_list: list[dict], _erp: EventResultPoster, description="推送领料申请至 T+"):
@async_rate_limit()
async def handle_pl_to_mo(event_data: dict, _aps: ApsPayloadSponsor):
"""处理PL类型变更:转为MO"""
try:
await TplusRs(event_data).create(
_aps=_aps,
_erp=_erp,
pydantic_model=_CustomRsPushModel,
)
except Exception as e:
CLIENT_LOGGER.fail("处理PL类型变更", str(event_data), str(e))
await _erp.rs_release_failed(rsno=supplyno, msg=str(e))
supply_nos = [s['supplyno'] for s in event_data_list]
aps_payload_sponsor = ApsPayloadSponsor(production_cache_items=[CacheItem.SUPPLY_MO, CacheItem.DEMAND, CacheItem.MATERIAL])
cache = await aps_payload_sponsor.establish_production_cache(supplynos=supply_nos)
await aps_payload_sponsor.establish_production_cache(supplynos=supply_nos)
_CustomRsPushModel = create_custom_rs_push_model(aps_payload_sponsor)
tasks = [handle_pl_to_mo(item, aps_payload_sponsor) for item in event_data_list]
tasks = [
TplusRs(event_data).create(
_aps=aps_payload_sponsor,
_erp=_erp,
pydantic_model=_CustomRsPushModel,
)
for event_data in event_data_list
]
await asyncio.gather(*tasks, return_exceptions=True)
@event_batch_handler(reminder=planner_email_reminder)
@async_rate_limit()
async def batch_handle_pr_status_a2e(pr_data_list: list[dict], _erp: EventResultPoster, description="推送请购单至 T+"):
try:
# try:
await TplusPr(pr_data_list).create(_erp=_erp)
except Exception as e:
pr_nos = [p.get('supplyno') for p in pr_data_list if p.get('supplyno')]
await _erp.pr_release_failed(prno=pr_nos[0] if pr_nos else None, msg=str(e))
# except Exception as e:
# CLIENT_LOGGER.warning_msg(f"{description}失败", str(e))
# pr_nos = [p.get('supplyno') for p in pr_data_list if p.get('supplyno')]
# await _erp.pr_release_failed(prno=pr_nos[0] if pr_nos else None, msg=str(e))
+2 -2
View File
@@ -20,8 +20,8 @@
"app_key": "UG69s1P1",
"app_secret": "F1A51CB0AD5275BF6FAA46270AF1CF37",
"refresh_token": "4c6ee04668b4467ca258792ce20425f3",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJpc3YiLCJpc3MiOiJjaGFuamV0IiwidXNlcklkIjoiMzk2MjUxNzMxNzA5OTU0Iiwib3JnSWQiOiIxMjQyMTYwNjg0MjM1NjIwIiwiYWNjZXNzX3Rva2VuIjoiYmMtZjhlMmU1MTEtNGQ4ZS00ZWNjLTg2OGQtNmU5YjNhMDYyMmM0IiwiYXVkIjoiaXN2IiwibmJmIjoxNzc3MjUxMzAxLCJhcHBJZCI6IjU4Iiwic2NvcGUiOiJhdXRoX2FsbCIsImFwcEtleSI6IlVHNjlzMVAxIiwiaWQiOiJiYmE0YzJhMC01YWQ3LTRmZmQtODVlYi1lNmI3MmEyOWM4MGUiLCJleHAiOjE3Nzc3Njk3MDEsImlhdCI6MTc3NzI1MTMwMSwib3JnQWNjb3VudCI6InV0dTJwb2ZhZDN1MiJ9.NX194P0Ue1TWKJA5q2vAsHLcxSv3XX4mcwVEZlPRjog",
"_auth_at_": "2026-04-27 08:55:01",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJpc3YiLCJpc3MiOiJjaGFuamV0IiwidXNlcklkIjoiMzk2MjUxNzMxNzA5OTU0Iiwib3JnSWQiOiIxMjQyMTYwNjg0MjM1NjIwIiwiYWNjZXNzX3Rva2VuIjoiYmMtZjhlMmU1MTEtNGQ4ZS00ZWNjLTg2OGQtNmU5YjNhMDYyMmM0IiwiYXVkIjoiaXN2IiwibmJmIjoxNzc3Mjk4MTczLCJhcHBJZCI6IjU4Iiwic2NvcGUiOiJhdXRoX2FsbCIsImFwcEtleSI6IlVHNjlzMVAxIiwiaWQiOiIzNmIzY2I0ZC00MmNkLTQ0OWQtOTA5Ni05NWRhOGYyN2FkYjYiLCJleHAiOjE3Nzc4MTY1NzMsImlhdCI6MTc3NzI5ODE3Mywib3JnQWNjb3VudCI6InV0dTJwb2ZhZDN1MiJ9.oUBpYsJnmyS0XONNnsATAafzczmaBF04uPSNLOW4FXQ",
"_auth_at_": "2026-04-27 21:56:13",
"$MoBusiType": "PT01",
"$PrRequisitionPerson": "001"
},
+23 -8
View File
@@ -1,5 +1,7 @@
@echo off
title MyAPS API SERVER
:: 启用延迟扩展以支持循环内变量
setlocal enabledelayedexpansion
:: 配置变量
set "VENV_PYTHON=%~dp0..\venv\Scripts\python.exe"
set "PROJECT_ROOT=%~dp0.."
@@ -10,19 +12,32 @@ set "RESTART_COUNT=0"
set "HOST=0.0.0.0"
set "PORT=8000"
set "PROJECT_DIR="
set "PROJECT_JSON="
:: 从 .env 文件读取 HOST 和 PORT 配置
echo Before reading .env: HOST=%HOST%, PORT=%PORT%
echo Before reading .env: HOST=%HOST%, PORT=%PORT%, PROJECT_JSON=%PROJECT_JSON%
if exist "%ENV_FILE%" (
echo Reading from .env file: %ENV_FILE%
for /f "usebackq tokens=1,2 delims==" %%a in ("%ENV_FILE%") do (
if /i "%%a"=="HOST" set "HOST=%%b"
if /i "%%a"=="PORT" set "PORT=%%b"
if /i "%%a"=="PROJECT_DIR" set "PROJECT_DIR=%%b"
if /i "%%a"=="PROJECT_JSON" set "PROJECT_JSON=%%b"
)
:: 读取 HOST
for /f "tokens=2 delims==" %%a in ('findstr /i "^HOST=" "%ENV_FILE%"') do set "HOST=%%a"
:: 读取 PORT
for /f "tokens=2 delims==" %%a in ('findstr /i "^PORT=" "%ENV_FILE%"') do set "PORT=%%a"
:: 读取 PROJECT_DIR
for /f "tokens=2 delims==" %%a in ('findstr /i "^PROJECT_DIR=" "%ENV_FILE%"') do set "PROJECT_DIR=%%a"
:: 读取 PROJECT_JSON
for /f "tokens=2 delims==" %%a in ('findstr /i "^PROJECT_JSON=" "%ENV_FILE%"') do set "PROJECT_JSON=%%a"
:: 去除所有变量的前后空格
for /f "tokens=* delims= " %%a in ("%HOST%") do set "HOST=%%a"
for /f "tokens=* delims= " %%a in ("%PORT%") do set "PORT=%%a"
for /f "tokens=* delims= " %%a in ("%PROJECT_DIR%") do set "PROJECT_DIR=%%a"
for /f "tokens=* delims= " %%a in ("%PROJECT_JSON%") do set "PROJECT_JSON=%%a"
)
echo After reading .env: HOST=%HOST%, PORT=%PORT%
echo After reading .env: HOST=%HOST%, PORT=%PORT%, PROJECT_JSON=%PROJECT_JSON%
:: 显示启动信息
echo =========================================