Skip to content

Commit 8525891

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 a125cd9 commit 8525891

File tree

10 files changed

+788
-3
lines changed

10 files changed

+788
-3
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
@@ -35,7 +35,11 @@ class BillableInvoice(invoice.Invoice):
3535
invoice.SU_HOURS_FIELD,
3636
invoice.SU_TYPE_FIELD,
3737
invoice.RATE_FIELD,
38+
invoice.GROUP_NAME_FIELD,
39+
invoice.GROUP_INSTITUTION_FIELD,
40+
invoice.GROUP_BALANCE_FIELD,
3841
invoice.COST_FIELD,
42+
invoice.GROUP_BALANCE_USED_FIELD,
3943
invoice.CREDIT_FIELD,
4044
invoice.CREDIT_CODE_FIELD,
4145
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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
validate_billable_pi_processor,
2323
new_pi_credit_processor,
2424
bu_subsidy_processor,
25+
prepayment_processor,
2526
)
2627

2728
### PI file field names
@@ -53,9 +54,8 @@
5354
###
5455

5556
PI_S3_FILEPATH = "PIs/PI.csv"
56-
57-
5857
ALIAS_S3_FILEPATH = "PIs/alias.csv"
58+
PREPAY_DEBITS_S3_FILEPATH = "Prepay/prepay_debits.csv"
5959

6060

6161
def load_alias(alias_file):
@@ -73,6 +73,14 @@ def load_alias(alias_file):
7373
return alias_dict
7474

7575

76+
def load_prepay_csv(prepay_credits_path, prepay_projects_path, prepay_contacts_path):
77+
return (
78+
pandas.read_csv(prepay_credits_path),
79+
pandas.read_csv(prepay_projects_path),
80+
pandas.read_csv(prepay_contacts_path),
81+
)
82+
83+
7684
def get_iso8601_time():
7785
return datetime.datetime.now().strftime("%Y%m%dT%H%M%SZ")
7886

@@ -117,6 +125,24 @@ def main():
117125
required=True,
118126
help="File containing list of projects that are non-billable within a specified duration",
119127
)
128+
parser.add_argument(
129+
"--prepay-credits",
130+
required=False,
131+
default="prepaid_credits.csv",
132+
help="CSV listing all prepay group credits. Defaults to 'prepaid_credits.csv'",
133+
)
134+
parser.add_argument(
135+
"--prepay-projects",
136+
required=False,
137+
default="prepaid_projects.csv",
138+
help="CSV listing all prepay group projects. Defaults to 'prepaid_projects.csv'",
139+
)
140+
parser.add_argument(
141+
"--prepay-contacts",
142+
required=False,
143+
default="prepaid_contacts.csv",
144+
help="CSV listing all prepay group contact information. Defaults to 'prepaid_contacts.csv'",
145+
)
120146

121147
parser.add_argument(
122148
"--nonbillable-file",
@@ -164,6 +190,11 @@ def main():
164190
required=False,
165191
help="Name of alias file listing PIs with aliases (and their aliases). If not provided, defaults to fetching from S3",
166192
)
193+
parser.add_argument(
194+
"--prepay-debits",
195+
required=False,
196+
help="Name of csv file listing all prepay group debits. If not provided, defaults to fetching from S3",
197+
)
167198
parser.add_argument(
168199
"--BU-subsidy-amount",
169200
required=True,
@@ -190,6 +221,15 @@ def main():
190221
alias_file = fetch_s3_alias_file()
191222
alias_dict = load_alias(alias_file)
192223

224+
if args.prepay_debits:
225+
prepay_debits_filepath = args.prepay_debits
226+
else:
227+
prepay_debits_filepath = fetch_s3_prepay_debits()
228+
229+
prepay_credits, prepay_projects, prepay_info = load_prepay_csv(
230+
args.prepay_credits, args.prepay_projects, args.prepay_contacts
231+
)
232+
193233
merged_dataframe = merge_csv(csv_files)
194234

195235
pi = []
@@ -248,7 +288,19 @@ def main():
248288
)
249289
bu_subsidy_proc.process()
250290

251-
processed_data = bu_subsidy_proc.data
291+
prepayment_proc = prepayment_processor.PrepaymentProcessor(
292+
"",
293+
invoice_month,
294+
bu_subsidy_proc.data,
295+
prepay_credits,
296+
prepay_projects,
297+
prepay_info,
298+
prepay_debits_filepath,
299+
args.upload_to_s3,
300+
)
301+
prepayment_proc.process()
302+
303+
processed_data = prepayment_proc.data
252304

253305
### Initialize invoices
254306

@@ -378,6 +430,13 @@ def fetch_s3_old_pi_file():
378430
return local_name
379431

380432

433+
def fetch_s3_prepay_debits():
434+
local_name = "prepay_debits.csv"
435+
invoice_bucket = util.get_invoice_bucket()
436+
invoice_bucket.download_file(PREPAY_DEBITS_S3_FILEPATH, local_name)
437+
return local_name
438+
439+
381440
def backup_to_s3_old_pi_file(old_pi_file):
382441
invoice_bucket = util.get_invoice_bucket()
383442
invoice_bucket.upload_file(old_pi_file, f"PIs/Archive/PI {get_iso8601_time()}.csv")

0 commit comments

Comments
 (0)