You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
4.4 KiB
156 lines
4.4 KiB
"""
|
|
Classes and functions to parse completed reconciliation reports and remember
|
|
the resolutions of contracts.
|
|
|
|
Also provides a way for the reconciler to check hold against previously
|
|
resolved holds.
|
|
|
|
*Last Updated: version 1.3
|
|
"""
|
|
from . import drop_unnamed
|
|
from ghlib.database.database_manager import SQLiteManager
|
|
|
|
from pandas import DataFrame, Series, read_sql_query, read_excel, concat
|
|
from logging import getLogger
|
|
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
|
def normalize_cols(df: DataFrame) -> DataFrame:
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def process_resolutions(df: DataFrame) -> DataFrame:
|
|
"""
|
|
|
|
"""
|
|
# Drop unnamed columns:
|
|
drop_unnamed(df) # Works 'inplace'
|
|
|
|
# Drop anything where resolution is blanks
|
|
df: DataFrame = df[~df["Resolution"].isnull()]
|
|
|
|
# Standardize the resolution
|
|
df["Resolution"] = df["Resolution"].astype(str)
|
|
df["Resolution"] = df["Resolution"].apply(lambda res: res.strip().lower())
|
|
|
|
# Check for multiple 'onhold_amount' columns
|
|
cols: list[str] = list(df.keys())
|
|
mult_amounts: bool = True if "onhold_amount_ob" in cols else False
|
|
|
|
if mult_amounts:
|
|
# Create duplicates with the other amounts
|
|
gp_amts: DataFrame = df[
|
|
["contract_number",
|
|
"onhold_amount_gp",
|
|
"Resolution",
|
|
"Notes"
|
|
]]
|
|
df = df[
|
|
["contract_number",
|
|
"onhold_amount_ob",
|
|
"Resolution",
|
|
"Notes"
|
|
]]
|
|
|
|
# Rename the amount columns and add the source
|
|
gp_amts.rename(columns={"onhold_amount_gp":"onhold_amount"}, inplace=True)
|
|
gp_amts["Source"] = "GP"
|
|
df.rename(columns={"onhold_amount_ob":"onhold_amount"}, inplace=True)
|
|
df["Source"] = "OB"
|
|
|
|
# Combine them back together
|
|
df: DataFrame = concat([df, gp_amts])
|
|
df["Type"] = "AmountMismatch"
|
|
|
|
else:
|
|
# Filter columns
|
|
df = df[
|
|
["Source",
|
|
"contract_number",
|
|
"onhold_amount",
|
|
"Resolution",
|
|
"Notes"
|
|
]]
|
|
df["Type"] = "NoMatch"
|
|
|
|
return df
|
|
|
|
|
|
def save_recs(resolved_dataframes: list[DataFrame]):
|
|
"""
|
|
"""
|
|
sqlManager: SQLiteManager = SQLiteManager("OnHold.db")
|
|
with sqlManager.get_session() as session:
|
|
conn = session.connection()
|
|
|
|
df: DataFrame
|
|
for df in resolved_dataframes:
|
|
try:
|
|
# Drop uneeded columns and filter only to resolved data
|
|
df = process_resolutions(df)
|
|
# Save to the database
|
|
df.to_sql("Resolutions", conn, if_exists="append")
|
|
except Exception as e:
|
|
logger.exception(f"Could not save resolution dataframe: {e}")
|
|
continue
|
|
|
|
|
|
def get_prev_reconciled(contracts: list[str]) -> DataFrame:
|
|
"""
|
|
Get a DataFrame of previously reconciled contracts from an SQLite database.
|
|
|
|
Args:
|
|
contracts (list[str]): A list of contract numbers to check for previously reconciled contracts.
|
|
|
|
Returns:
|
|
DataFrame: A DataFrame of previously reconciled contracts, or an empty DataFrame if none are found.
|
|
"""
|
|
# Create a DB manager
|
|
sqlManager: SQLiteManager = SQLiteManager("OnHold.db")
|
|
|
|
# Create a temp table to hold this batches contract numbers
|
|
# this table will be cleared when sqlManager goes out of scope
|
|
temp_table_statement = """
|
|
CREATE TEMPORARY TABLE CUR_CONTRACTS (contract_numbers VARCHAR(11));
|
|
"""
|
|
sqlManager.execute(temp_table_statement)
|
|
|
|
# Insert the current contracts into the temp table
|
|
insert_contracts = f"""
|
|
INSERT INTO CUR_CONTRACTS (contract_numbers) VALUES
|
|
{', '.join([f"('{cn}')" for cn in contracts])};
|
|
"""
|
|
sqlManager.execute(insert_contracts)
|
|
|
|
# Select previously resolved contracts
|
|
res_query = """
|
|
SELECT r.*
|
|
FROM Resolutions r
|
|
JOIN CUR_CONTRACTS t
|
|
ON r.contract_number = t.contract_number;
|
|
"""
|
|
resolved: DataFrame = sqlManager.execute(res_query, as_dataframe=True)
|
|
return resolved
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(
|
|
prog="HoldReconcilerRecord",
|
|
)
|
|
parser.add_argument("-i", "--input")
|
|
args = parser.parse_args()
|
|
|
|
# No Match
|
|
no_match: DataFrame = read_excel(args.input, sheet_name="No Match")
|
|
# Amount Mismatch
|
|
amt_mm: DataFrame = read_excel(args.input, sheet_name="Amount Mismatch")
|
|
|
|
save_recs(resolved_dataframes=[no_match, amt_mm]) |