AutoRpa/rpa_contract/repo.py

239 lines
7.4 KiB
Python

"""表 3 领取、表 4 写入。"""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from datetime import date
from decimal import Decimal
from typing import Any
from db import RepoUnavailable, connect_from_env, oracle_dsn
@dataclass
class PurchaseContractRow:
fid: int
oms_id: int
supplier_id: str
supplier_name: str
contract_sign_date: date | None
contract_start_date: date | None
contract_end_date: date | None
payment_term: int
goods_id: str
goods_name: str
reg_no: str
spec: str
unit: str
qty: Decimal | float | int
unit_price: Decimal | float | int
purchaser_id: str
purchaser_name: str
planner_id: str
planner_name: str
contract_entry_status: int | None
contract_no: str | None
process_status: int
org_code: str | None
goods_mdm_code: str | None
supplier_mdm_code: str | None
process_result: str = ""
class MemoryContractRepo:
def __init__(self, rows: list[PurchaseContractRow]):
self.table3 = {row.fid: row for row in rows}
self.table4: list[dict] = []
self._next_fid = 1
def claim_pending(self, limit: int | None = None) -> list[PurchaseContractRow]:
from group import is_claimable
pending = sorted(
(row for row in self.table3.values() if is_claimable(row)),
key=lambda row: row.fid,
)
if limit is not None:
pending = pending[:limit]
for row in pending:
row.process_status = 3
return pending
def save_group(self, payload: Any) -> None:
for row in payload.table4_rows:
item = dict(row)
item["fid"] = self._next_fid
self._next_fid += 1
self.table4.append(item)
for fid in payload.fids:
target = self.table3[fid]
target.process_status = payload.process_status
target.process_result = payload.process_result
_SELECT_PENDING = """
SELECT
fid, oms_id, supplier_id, supplier_name,
contract_sign_date, contract_start_date, contract_end_date,
payment_term, goods_id, goods_name, reg_no, spec, unit,
qty, unit_price, purchaser_id, purchaser_name, planner_id, planner_name,
contract_entry_status, contract_no, process_status, org_code,
goods_mdm_code, supplier_mdm_code,
NVL(process_result, '') AS process_result
FROM rpa_purchase_contract_data
WHERE process_status = 0
AND NOT (NVL(contract_entry_status, -1) = 0
AND contract_no IS NOT NULL
AND LENGTH(TRIM(contract_no)) > 0)
ORDER BY fid
"""
_SELECT_PENDING_LIMIT = f"""
SELECT * FROM (
{_SELECT_PENDING}
) WHERE ROWNUM <= :limit
"""
_CLAIM_SQL = """
UPDATE rpa_purchase_contract_data
SET process_status = 3,
process_time = SYSTIMESTAMP,
update_time = SYSTIMESTAMP
WHERE fid = :fid AND process_status = 0
"""
_INSERT_TABLE4_SQL = """
INSERT INTO rpa_contract_agreement_result (
oms_id, action_code, contract_status, contract_fail_reason, contract_no,
agreement_status, agreement_fail_reason, agreement_id, agreement_dtl_id,
push_status, create_time, update_time,
create_user_id, create_user_name, update_user_id, update_user_name, org_code
) VALUES (
:oms_id, :action_code, :contract_status, :contract_fail_reason, :contract_no,
:agreement_status, :agreement_fail_reason, :agreement_id, :agreement_dtl_id,
0, SYSTIMESTAMP, SYSTIMESTAMP,
0, '合同班', 0, '合同班', :org_code
)
"""
_COMPLETE_SQL = """
UPDATE rpa_purchase_contract_data
SET process_status = :process_status,
process_result = :process_result,
process_time = SYSTIMESTAMP,
update_time = SYSTIMESTAMP
WHERE fid = :fid AND process_status = 3
"""
_TABLE4_BIND_KEYS = (
"oms_id",
"action_code",
"contract_status",
"contract_fail_reason",
"contract_no",
"agreement_status",
"agreement_fail_reason",
"agreement_id",
"agreement_dtl_id",
"org_code",
)
def _row_from_record(cols: list[str], rec) -> PurchaseContractRow:
data = dict(zip(cols, rec))
return PurchaseContractRow(
fid=int(data["fid"]),
oms_id=int(data["oms_id"]),
supplier_id=data["supplier_id"],
supplier_name=data["supplier_name"],
contract_sign_date=data["contract_sign_date"],
contract_start_date=data["contract_start_date"],
contract_end_date=data["contract_end_date"],
payment_term=int(data["payment_term"]),
goods_id=data["goods_id"],
goods_name=data["goods_name"],
reg_no=data["reg_no"],
spec=data["spec"],
unit=data["unit"],
qty=data["qty"],
unit_price=data["unit_price"],
purchaser_id=data["purchaser_id"],
purchaser_name=data["purchaser_name"],
planner_id=data["planner_id"],
planner_name=data["planner_name"],
contract_entry_status=data["contract_entry_status"],
contract_no=data["contract_no"],
process_status=int(data["process_status"]),
org_code=data["org_code"],
goods_mdm_code=data["goods_mdm_code"],
supplier_mdm_code=data["supplier_mdm_code"],
process_result=data.get("process_result") or "",
)
class OracleContractRepo:
def __init__(self, connect: Callable):
self._connect = connect
@classmethod
def from_env(cls) -> "OracleContractRepo":
oracle_dsn()
return cls(connect=connect_from_env)
def claim_pending(self, limit: int | None = None) -> list[PurchaseContractRow]:
conn = self._connect()
try:
with conn.cursor() as cur:
if limit is None:
cur.execute(_SELECT_PENDING)
else:
cur.execute(_SELECT_PENDING_LIMIT, {"limit": int(limit)})
cols = [item[0].lower() for item in cur.description]
fetched = [_row_from_record(cols, rec) for rec in cur.fetchall()]
claimed: list[PurchaseContractRow] = []
for row in fetched:
cur.execute(_CLAIM_SQL, {"fid": row.fid})
if cur.rowcount == 1:
row.process_status = 3
claimed.append(row)
conn.commit()
return claimed
except Exception as exc:
try:
conn.rollback()
except Exception:
pass
raise RepoUnavailable(str(exc)) from exc
finally:
conn.close()
def save_group(self, payload: Any) -> None:
conn = self._connect()
try:
with conn.cursor() as cur:
for row in payload.table4_rows:
cur.execute(
_INSERT_TABLE4_SQL,
{key: row.get(key) for key in _TABLE4_BIND_KEYS},
)
for fid in payload.fids:
cur.execute(
_COMPLETE_SQL,
{
"process_status": payload.process_status,
"process_result": payload.process_result,
"fid": fid,
},
)
conn.commit()
except Exception as exc:
try:
conn.rollback()
except Exception:
pass
raise RepoUnavailable(str(exc)) from exc
finally:
conn.close()