Skip to content

Commit 6e2cacf

Browse files
committed
Implemented processor for prepayments
Prepayments have been implemented by a new processor, `PrepaymentProcessor`. The full implementation for prepayments involved the following changes: - Several new columns names have been added to `invoices/invoice.py`, including column names for the prepay data files and for the exported invoices - Many invoices will now export with 4 new columns: `Prepaid Group Name`, `Prepaid Group Institution`, `Prepaid Group Balance`, `Prepaid Group Used` - 4 command line arguments have been added to `process_report.py`. 3 of them allows the user to pass in a local version of the prepay credits, contacts, and projects file. The last one (`—prepay-debits`) allows passing a local version of the prepay debits files, and defaults to fetching from s3 if not provided - A set of test cases have been added for `PrepaymentProcessor` Since the implementation of this feature required a lot of logic decisions (i.e What happens if a prepaid project is active, but has no funds?), below is (hopefully) an exhaustive list of code logic decisions that were made. These can also be inferred through the test cases. - Prepay projects listed in `prepaid_projects.csv` are identified by their project name, not project - allocation name - Attempting to process past invoices (“backprocessing”) may result in incorrect output due to the nature of the prepay debit ledger - While backprocessing is not supported for past months, processing the same invoice month twice will still return correct output. In this case, the month’s debit entry may be will be overwritten - Prepay balances can be used in the same month they are added. - The time range in which prepay projects are considered “active” includes their start and end date - After processing of any given invoice month, debit entries for that month will be added. I emphasize this for clarification. A debit entry such as: `2024-11,G1,1062.48` Should be interpreted as: In the period from 2024-11-01 to 2024-11-30, prepay group G1 spent $1062.48 As opposed to: In the period from 2024-10-01 to 2024-10-31, … - If prepay projects are “active” but their prepay group has $0 balance, their prepay info (group name, contact email) is still included, but the prepay balance will be displayed as $0 and the prepay used as an empty field
1 parent 5c7290d commit 6e2cacf

File tree

10 files changed

+794
-2
lines changed

10 files changed

+794
-2
lines changed

process_report/invoices/NERC_total_invoice.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ class NERCTotalInvoice(invoice.Invoice):
3030
invoice.SU_HOURS_FIELD,
3131
invoice.SU_TYPE_FIELD,
3232
invoice.RATE_FIELD,
33+
invoice.GROUP_NAME_FIELD,
34+
invoice.GROUP_INSTITUTION_FIELD,
35+
invoice.GROUP_BALANCE_FIELD,
3336
invoice.COST_FIELD,
37+
invoice.GROUP_BALANCE_USED_FIELD,
3438
invoice.CREDIT_FIELD,
3539
invoice.CREDIT_CODE_FIELD,
3640
invoice.BALANCE_FIELD,

process_report/invoices/billable_invoice.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ class BillableInvoice(invoice.Invoice):
3131
invoice.SU_HOURS_FIELD,
3232
invoice.SU_TYPE_FIELD,
3333
invoice.RATE_FIELD,
34+
invoice.GROUP_NAME_FIELD,
35+
invoice.GROUP_INSTITUTION_FIELD,
36+
invoice.GROUP_BALANCE_FIELD,
3437
invoice.COST_FIELD,
38+
invoice.GROUP_BALANCE_USED_FIELD,
3539
invoice.CREDIT_FIELD,
3640
invoice.CREDIT_CODE_FIELD,
3741
invoice.BALANCE_FIELD,

process_report/invoices/bu_internal_invoice.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ class BUInternalInvoice(invoice.Invoice):
1515
invoice.INVOICE_DATE_FIELD,
1616
invoice.PI_FIELD,
1717
"Project",
18+
invoice.GROUP_NAME_FIELD,
19+
invoice.GROUP_INSTITUTION_FIELD,
20+
invoice.GROUP_BALANCE_FIELD,
1821
invoice.COST_FIELD,
22+
invoice.GROUP_BALANCE_USED_FIELD,
1923
invoice.CREDIT_FIELD,
2024
invoice.SUBSIDY_FIELD,
2125
invoice.PI_BALANCE_FIELD,

process_report/invoices/invoice.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@
1212
PI_2ND_USED = "2nd Month Used"
1313
###
1414

15+
### Prepay files fields
16+
PREPAY_MONTH_FIELD = "Month"
17+
PREPAY_CREDIT_FIELD = "Credit"
18+
PREPAY_DEBIT_FIELD = "Debit"
19+
PREPAY_GROUP_NAME_FIELD = "Group Name"
20+
PREPAY_GROUP_CONTACT_FIELD = "Group Contact Email"
21+
PREPAY_MANAGED_FIELD = "MGHPCC Managed"
22+
PREPAY_PROJECT_FIELD = "Project"
23+
PREPAY_START_DATE_FIELD = "Start Date"
24+
PREPAY_END_DATE_FIELD = "End Date"
25+
###
26+
1527
### Invoice field names
1628
INVOICE_DATE_FIELD = "Invoice Month"
1729
PROJECT_FIELD = "Project - Allocation"
@@ -21,6 +33,10 @@
2133
INVOICE_ADDRESS_FIELD = "Invoice Address"
2234
INSTITUTION_FIELD = "Institution"
2335
INSTITUTION_ID_FIELD = "Institution - Specific Code"
36+
GROUP_NAME_FIELD = "Prepaid Group Name"
37+
GROUP_INSTITUTION_FIELD = "Prepaid Group Institution"
38+
GROUP_BALANCE_FIELD = "Prepaid Group Balance"
39+
GROUP_BALANCE_USED_FIELD = "Prepaid Group Used"
2440
SU_HOURS_FIELD = "SU Hours (GBhr or SUhr)"
2541
SU_TYPE_FIELD = "SU Type"
2642
SU_CHARGE_FIELD = "SU Charge"
@@ -38,6 +54,7 @@
3854
MISSING_PI_FIELD = "Missing PI"
3955
PI_BALANCE_FIELD = "PI Balance"
4056
PROJECT_NAME_FIELD = "Project"
57+
GROUP_MANAGED_FIELD = "MGHPCC Managed"
4158
###
4259

4360

process_report/invoices/pi_specific_invoice.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ class PIInvoice(invoice.Invoice):
2727
invoice.SU_HOURS_FIELD,
2828
invoice.SU_TYPE_FIELD,
2929
invoice.RATE_FIELD,
30+
invoice.GROUP_NAME_FIELD,
31+
invoice.GROUP_INSTITUTION_FIELD,
32+
invoice.GROUP_BALANCE_FIELD,
3033
invoice.COST_FIELD,
34+
invoice.GROUP_BALANCE_USED_FIELD,
3135
invoice.CREDIT_FIELD,
3236
invoice.CREDIT_CODE_FIELD,
3337
invoice.BALANCE_FIELD,

process_report/process_report.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
validate_billable_pi_processor,
2424
new_pi_credit_processor,
2525
bu_subsidy_processor,
26+
prepayment_processor,
2627
)
2728

2829
### PI file field names
@@ -54,8 +55,8 @@
5455
###
5556

5657
PI_S3_FILEPATH = "PIs/PI.csv"
57-
5858
ALIAS_S3_FILEPATH = "PIs/alias.csv"
59+
PREPAY_DEBITS_S3_FILEPATH = "Prepay/prepay_debits.csv"
5960

6061

6162
logger = logging.getLogger(__name__)
@@ -77,6 +78,14 @@ def load_alias(alias_file):
7778
return alias_dict
7879

7980

81+
def load_prepay_csv(prepay_credits_path, prepay_projects_path, prepay_contacts_path):
82+
return (
83+
pandas.read_csv(prepay_credits_path),
84+
pandas.read_csv(prepay_projects_path),
85+
pandas.read_csv(prepay_contacts_path),
86+
)
87+
88+
8089
def get_iso8601_time():
8190
return datetime.datetime.now().strftime("%Y%m%dT%H%M%SZ")
8291

@@ -121,6 +130,24 @@ def main():
121130
required=True,
122131
help="File containing list of projects that are non-billable within a specified duration",
123132
)
133+
parser.add_argument(
134+
"--prepay-credits",
135+
required=False,
136+
default="prepaid_credits.csv",
137+
help="CSV listing all prepay group credits. Defaults to 'prepaid_credits.csv'",
138+
)
139+
parser.add_argument(
140+
"--prepay-projects",
141+
required=False,
142+
default="prepaid_projects.csv",
143+
help="CSV listing all prepay group projects. Defaults to 'prepaid_projects.csv'",
144+
)
145+
parser.add_argument(
146+
"--prepay-contacts",
147+
required=False,
148+
default="prepaid_contacts.csv",
149+
help="CSV listing all prepay group contact information. Defaults to 'prepaid_contacts.csv'",
150+
)
124151

125152
parser.add_argument(
126153
"--nonbillable-file",
@@ -168,6 +195,11 @@ def main():
168195
required=False,
169196
help="Name of alias file listing PIs with aliases (and their aliases). If not provided, defaults to fetching from S3",
170197
)
198+
parser.add_argument(
199+
"--prepay-debits",
200+
required=False,
201+
help="Name of csv file listing all prepay group debits. If not provided, defaults to fetching from S3",
202+
)
171203
parser.add_argument(
172204
"--BU-subsidy-amount",
173205
required=True,
@@ -194,6 +226,15 @@ def main():
194226
alias_file = fetch_s3_alias_file()
195227
alias_dict = load_alias(alias_file)
196228

229+
if args.prepay_debits:
230+
prepay_debits_filepath = args.prepay_debits
231+
else:
232+
prepay_debits_filepath = fetch_s3_prepay_debits()
233+
234+
prepay_credits, prepay_projects, prepay_info = load_prepay_csv(
235+
args.prepay_credits, args.prepay_projects, args.prepay_contacts
236+
)
237+
197238
merged_dataframe = merge_csv(csv_files)
198239

199240
pi = []
@@ -255,7 +296,19 @@ def main():
255296
)
256297
bu_subsidy_proc.process()
257298

258-
processed_data = bu_subsidy_proc.data
299+
prepayment_proc = prepayment_processor.PrepaymentProcessor(
300+
"",
301+
invoice_month,
302+
bu_subsidy_proc.data,
303+
prepay_credits,
304+
prepay_projects,
305+
prepay_info,
306+
prepay_debits_filepath,
307+
args.upload_to_s3,
308+
)
309+
prepayment_proc.process()
310+
311+
processed_data = prepayment_proc.data
259312

260313
### Initialize invoices
261314

@@ -385,6 +438,13 @@ def fetch_s3_old_pi_file():
385438
return local_name
386439

387440

441+
def fetch_s3_prepay_debits():
442+
local_name = "prepay_debits.csv"
443+
invoice_bucket = util.get_invoice_bucket()
444+
invoice_bucket.download_file(PREPAY_DEBITS_S3_FILEPATH, local_name)
445+
return local_name
446+
447+
388448
def backup_to_s3_old_pi_file(old_pi_file):
389449
invoice_bucket = util.get_invoice_bucket()
390450
invoice_bucket.upload_file(old_pi_file, f"PIs/Archive/PI {get_iso8601_time()}.csv")

0 commit comments

Comments
 (0)