From bc7fc340f5dd06a099e198f3fd646cc23d790c7d Mon Sep 17 00:00:00 2001 From: kououken Date: Wed, 20 Feb 2019 16:20:44 -0800 Subject: [PATCH 1/3] Initial update of policy file. --- back/backend/policy.py | 192 ++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 59 deletions(-) diff --git a/back/backend/policy.py b/back/backend/policy.py index 9808475..2674cc7 100644 --- a/back/backend/policy.py +++ b/back/backend/policy.py @@ -68,107 +68,181 @@ pol = Policy() #### Section 0 general_section = Section( title="General Info", - html_description="", + html_description="

Each section of this report is designed to guide you through the reimbursement process. Please read through each and answer as many questions as you can that apply to you.

Be sure to click 'Save' after completing each section. Your entered data will be saved as you progress. You may also receive feedback from sections regarding policy restrictions and special requirements.

", fields={ - "destination": {"number": 0, "label": "Destination City", "field_type": "string"}, + "before_trip": {"number": 0, "label": "Have you taken this trip already?", "field_type": "boolean"}, } ) general_section.add_rule( - title="Destination city check", - rule=lambda report, fields: fields['destination'] == "Timbuktu", - rule_break_text="What did the cowboy say about Tim, his wild horse?" + title="Pre-trip / post-trip check", + rule=lambda report, fields: None if fields['before_trip'] else "If you have already take the trip your request will require special approval by the administrator. You may skip the following 'Pre-trip Planning' section." ) pol.add_section(general_section) -#### Flight +#### Pre-trip Planning #### Section 1 -flight_section = Section( - title="Flight Info", - html_description="

Enter flight details here.

", + +planning_section = Section( + title="Pre-trip Planning", + html_description="

At least 14 days before buying tickets for your trip, take a screenshot of a flight search showing the least expensive fare available for the dates you need to travel. Include fares from multiple airlines if possible. This information will be used to calculate reimbursable fare amounts.

", fields={ - "international": {"number": 0, "label": "Is this an international flight?", "field_type": "boolean"}, - "departure_date": {"number": 1, "label": "Departure date", "field_type": "date"}, - "return_date": {"number": 2, "label": "Return date", "field_type": "date"}, - "fare": {"number": 3, "label": "Fare", "field_type": "decimal"}, - "layovers": {"number": 4, "label": "Transit wait", "field_type": "integer"}, - "fare_search_screenshot": {"number": 5, "label": "Screenshot of fare search", "field_type": "file"}, + "departure_date": {"number": 0, "label": "Departure date", "field_type": "date"}, + "return_date": {"number": 1, "label": "Return date", "field_type": "date"}, + "screenshot": {"number": 2, "label": "Screenshot of least expensive ticket fare", "field_type": "file"}, + "screenshot_date": {"number": 3, "label": "Date of screenshot", "field_type": "date"}, + "lowest_fare": {"number": 4, "label": "Lowest fare", "field_type": "decimal"}, + "lowest_fare_duration": {"number": 5, "label": "Flight duration of lowest fare (hours)", "field_type": "decimal"}, + "preferred_flight_fare": {"number": 6, "label": "Fare of your preferred flight", "field_type": "decimal"}, + "preferred_flight_duration": {"number": 7, "label": "Flight duration of your preferred flight (hours)", "field_type": "decimal"}, + "international_flight": {"number": 8, "label": "Is this an international flight?", "field_type": "boolean"}, } ) -flight_section.add_rule( - title="Airline fare pre-approval check", - rule=lambda report, fields: fields['fare'] < 500, - rule_break_text="Fares cannot be more than $500" +def fare_limit_rule(report, fields): + intl_flight = fields['international_flight'] + fare = fields['preferred_flight_fare'] + if intl_flight: + if fare > 1650: + return "Fares for international flights over 1,650 USD require Conservancy pre-approval, even if other policy conditions have been met." + else: + if fare > 750: + return "Fares for domestic flights over 750 USD require Conservancy pre-approval, even if other policy conditions have been met." + return None + +planning_section.add_rule(title="Fare limits", rule=fare_limit_rule) + +def lowest_fare_rule(report, fields): + diff = field['lowest_fare_duration'] - field['preferred_flight_duration'] + lowest_Fare = field['lowest_fare'] + maximum = 0 + if diff <= 0: + maximum = lowest_fare + 100 + elif diff <= 3: + maximum = lowest_fare + 100 + elif diff <= 6: + maximum = lowest_fare + 200 + elif diff <= 10: + maximum = lowest_fare + 350 + else: + maximum = lowest_fare + 600 + if field['preferred_fare'] > maximum: + return "For the lowest fare you have provided, your maximum in-policy fare amount is {} USD.".format(maximum) + return None + +planning_section.add_rule(title="Lowest fare check", rule=lowest_fare_rule) + +def departure_date_limit_rule(report, fields): + days_to_departure = date(field['departure_date']) - date(field['screenshot_date']) + if days_to_departure < 14: + return "Flights must be booked at least 14 days in advance." + if days_to_departure > 365: + return "Flights must be booked no more than 365 days in advance." + return None + +planning_section.add_rule(title="Departure date limit", rule=departure_date_limit_rule) +pol.add_section(planning_section) + +#### Flight Info +#### Section 2 + +flight_section = Section( + title="Flight Info", + html_description="

Enter the details of your flight once you have made your purchase.

", + fields={ + "departure_date": {"number": 0, "label": "Actual departure date", "field_type": "date"}, + "return_date": {"number": 1, "label": "Actual return date", "field_type": "date"}, + "fare": {"number": 2, "label": "Ticket fare", "field_type": "decimal"}, + "confirmation_screenshot": {"number": 3, "label": "Screenshot of confirmation of purchase", "field_type": "file"}, + "international_flight": {"number": 4, "label": "Was this an international flight?", "field_type": "boolean"}, + } ) +def actual_fare_limit_rule(report, fields): + intl_flight = fields['international_flight'] + fare = fields['fare'] + if intl_flight: + if fare > 1650: + return "Fares for international flights over 1,650 USD require Conservancy pre-approval, even if other policy conditions have been met." + else: + if fare > 750: + return "Fares for domestic flights over 750 USD require Conservancy pre-approval, even if other policy conditions have been met." + return None + +flight_section.add_rule(title="Fare limits", rule=actual_fare_limit_rule) + +def request_date_rule(report, fields): + now = date.today() + last_travel = date(field['return_date']) + if now - last_travel > 90: + return "Reimbursement requests must be made within 90 days of the last day of travel." + return None + +flight_section.add_rule(title="Request date", rule=request_date_rule) pol.add_section(flight_section) -#### Lodging -#### Section 2 +#### Hotels / Lodging +#### Section 3 + lodging_section = Section( - title="Hotel Info", - html_description="

Enter hotel info here.\nPer diem rates can be found at " - "this link

", + title="Hotel / Lodging", + html_description="

Please submit a receipt from your hotel including both the total amount and the dates of your stay. Per diem rates can be found on the U.S. GSA website.

", fields={ - "check-in_date": {"number": 0, "label": "Check-in date", "field_type": "date"}, - "check-out_date": {"number": 1, "label": "Check-out date", "field_type": "date"}, - "rate": {"number": 2, "label": "Per diem nightly rate", "field_type": "decimal"}, - "cost": {"number": 3, "label": "Total Cost", "field_type": "decimal"} + "per_diem_rate": {"number": 0, "label": "USGSA Per diem rate", "field_type": "decimal"}, + "cost": {"number": 1, "label": "Total cost for lodging", "field_type": "decimal"}, + "check_in_date": {"number": 2, "label": "Check-in date", "field_type": "date"}, + "check_out_date": {"number": 3, "label": "Check-out date", "field_type": "date"}, + "invoice_screenshot": {"number": 4, "label": "Screenshot of invoice", "field_type": "screenshot"}, } ) def nightly_rate_check(report, fields): - checkin_date = date(fields['checkin_date']) - checkout_date = date(fields['checkout_date']) + checkin_date = date(fields['check_in_date']) + checkout_date = date(fields['check_out_date']) duration = checkout_date - checkin_date - return fields['cost'] <= duration * fields['rate'] + if fields['cost'] > duration * fields['per_diem_rate']: + return "The average nightly rate cannot exceed the U.S. GSA rate." + return None -lodging_section.add_rule( - title="Average nightly rate", - rule=nightly_rate_check, - rule_break_text="The average nightly rate cannot be more than the USGSA rate." -) +lodging_section.add_rule(title="Average nightly rate", rule=nightly_rate_check) pol.add_section(lodging_section) #### Local Transportation -#### Section 3 +#### Section 4 + transport_section = Section( title="Local Transportation", - html_description="

How much did you spend on local transportation, in total?

", + html_description="

This amount includes taxis, uber, and public transportation.

", fields={ - "duration": {"number":0, "label": "How many days was your trip?", "field_type": "integer"}, - "cost": {"number":1, "label": "Total cost", "field_type": "decimal"} + "cost": {"number":0, "label": "Total cost of local transportation", "field_type": "decimal"} } ) -transport_section.add_rule( - title="Total cost check", - rule=lambda report, fields: fields['cost'] <= fields['duration'] * 10, - rule_break_text="Local transportation costs must be less than $10 per day, on average." -) - pol.add_section(transport_section) -#### Per Diem -#### Section 4 +#### Per Diem and Other Expenses +#### Section 5 + per_diem_section = Section( - title="Per Diem", - html_description="

Enter info about meals and incidentals here.\nPer diem rates can be found at " - "this link

", + title="Per Diem and Other Expenses", + html_description="

Your per diem allowance is used to cover meals and incidental expenses. The rate for your travel destination can be found on the following websites:

You may request up to 100% of the listed rate for a full day of travel, or 75% for a partial day of travel.", fields={ - "duration": {"number":0, "label": "How many days was your trip?", "field_type": "integer"}, - "rate": {"number":1, "label": "What is the per diem rate for your destination?", "field_type": "decimal"}, - "cost": {"number":2,"label": "Total Cost for meals and incidentals", "field_type": "decimal"} + "rate": {"number":0, "label": "Per diem rate", "field_type": "decimal"}, + "full_days": {"number":1, "label": "Number of full days of travel", "field_type": "integer"}, + "partial_days": {"number":2, "label": "Number of partial days of travel", "field_type": "integer"}, + "cost": {"number":3, "label": "Total Cost for meals and incidentals", "field_type": "decimal"} } ) -per_diem_section.add_rule( - title="Per Diem Cost Check", - rule=lambda report, fields: fields['cost'] <= fields['duration'] * fields['rate'], - rule_break_text="The average cost per day for per diem expenses cannot be more than the rate specified by the USGSA." -) +def incidentals_rule(report, fields): + rate = fields['rate'] + maximum = fields['full_days'] * rate + fields['partial_days'] * .75 * rate + if fields['cost'] > maximum: + return "You may only request a maximum of {} USD for the rate and trip duration provided.".format(maximum) + return None + +per_diem_section.add_rule(title="Per diem check", rule=incidentals_rule) pol.add_section(per_diem_section) From 9ea335d68f998fd36e344deec2bf8959a813e6c1 Mon Sep 17 00:00:00 2001 From: kououken Date: Wed, 20 Feb 2019 17:05:36 -0800 Subject: [PATCH 2/3] Fixed bugs in rules returning strings. --- back/backend/models.py | 1 + back/backend/policy.py | 6 +++--- back/backend/views.py | 10 ++++++---- back/db.sqlite3 | Bin 105472 -> 105472 bytes 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/back/backend/models.py b/back/backend/models.py index 7859a08..69db8ee 100644 --- a/back/backend/models.py +++ b/back/backend/models.py @@ -79,6 +79,7 @@ class Field(models.Model): return "{}".format(self.data_string) elif self.field_type == "integer": return "{}".format(self.data_integer) + return "Invalid type" def get_datatype(self): """ diff --git a/back/backend/policy.py b/back/backend/policy.py index 2674cc7..df95286 100644 --- a/back/backend/policy.py +++ b/back/backend/policy.py @@ -70,13 +70,13 @@ general_section = Section( title="General Info", html_description="

Each section of this report is designed to guide you through the reimbursement process. Please read through each and answer as many questions as you can that apply to you.

Be sure to click 'Save' after completing each section. Your entered data will be saved as you progress. You may also receive feedback from sections regarding policy restrictions and special requirements.

", fields={ - "before_trip": {"number": 0, "label": "Have you taken this trip already?", "field_type": "boolean"}, + "after_trip": {"number": 0, "label": "Have you taken this trip already?", "field_type": "boolean"}, } ) general_section.add_rule( title="Pre-trip / post-trip check", - rule=lambda report, fields: None if fields['before_trip'] else "If you have already take the trip your request will require special approval by the administrator. You may skip the following 'Pre-trip Planning' section." + rule=lambda report, fields: "If you have already take the trip your request will require special approval by the administrator. You may skip the following 'Pre-trip Planning' section." if fields['after_trip'] else None ) pol.add_section(general_section) @@ -193,7 +193,7 @@ lodging_section = Section( "cost": {"number": 1, "label": "Total cost for lodging", "field_type": "decimal"}, "check_in_date": {"number": 2, "label": "Check-in date", "field_type": "date"}, "check_out_date": {"number": 3, "label": "Check-out date", "field_type": "date"}, - "invoice_screenshot": {"number": 4, "label": "Screenshot of invoice", "field_type": "screenshot"}, + "invoice_screenshot": {"number": 4, "label": "Screenshot of invoice", "field_type": "file"}, } ) diff --git a/back/backend/views.py b/back/backend/views.py index 1216bee..bd4a9ec 100644 --- a/back/backend/views.py +++ b/back/backend/views.py @@ -53,10 +53,11 @@ def get_sections(r_id): for rule in rules: try: named_fields = generate_named_fields_for_section(data['fields']) - if not rule['rule'](data, named_fields): + result = rule['rule'](data, named_fields) + if not result is None: info = { "label": rule['title'], - "rule_break_text": rule['rule_break_text'], + "rule_break_text": result, } data['rule_violations'].append(info) except Exception as e: @@ -342,10 +343,11 @@ def section(request, report_pk, section_pk): for rule in rules: try: named_fields = generate_named_fields_for_section(data['fields']) - if not rule['rule'](data, named_fields): + result = rule['rule'](data, named_fields) + if not result is None: info = { "label": rule['title'], - "rule_break_text": rule['rule_break_text'], + "rule_break_text": result, } data['rule_violations'].append(info) except Exception as e: diff --git a/back/db.sqlite3 b/back/db.sqlite3 index d3fa2278ce91b3a2bb1f2be0c6d40086c9436469..b0936b92192c4608e786589b5bcca9f75278d50c 100644 GIT binary patch delta 8425 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 delta 6735 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 Yu))Q~?uBcgay3Jy`v9cGkKnENKQ-m_fdBvi From a07258c1f501545f5d535fe9cf547853105c9c8a Mon Sep 17 00:00:00 2001 From: kououken Date: Wed, 20 Feb 2019 17:38:57 -0800 Subject: [PATCH 3/3] All sections added to policy file. --- back/backend/policy.py | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/back/backend/policy.py b/back/backend/policy.py index df95286..6366018 100644 --- a/back/backend/policy.py +++ b/back/backend/policy.py @@ -246,3 +246,59 @@ def incidentals_rule(report, fields): per_diem_section.add_rule(title="Per diem check", rule=incidentals_rule) pol.add_section(per_diem_section) + +#### Payment Option - Paypal +#### Section 6 + +paypal_section = Section( + title="Payment Option - Paypal", + html_description="

Complete this section if you wish to be reimbursed via Paypal. This is the preferred reimbursement method of Software Freedom Conservancy.

", + fields={ + "paypal_email": {"number":0, "label":"Email address used with Paypal", "field_type":"string"}, + "preferred_currency": {"number":1, "label":"Preferred currency", "field_type":"string"}, + } +) + +pol.add_section(paypal_section) + +#### Payment Option - Check +#### Section 7 + +check_section = Section( + title="Payment Option - Check", + html_description="

Complete this section if you wish to be reimbursed in USD via check sent by mail.

", + fields={ + "address_1": {"number":0, "label":"Street address", "field_type":"string"}, + "address_2": {"number":1, "label":"Street address 2", "field_type":"string"}, + "city": {"number":2, "label":"City", "field_type":"string"}, + "state": {"number":3, "label":"State", "field_type":"string"}, + "zip": {"number":4, "label":"Zip code", "field_type":"string"}, + } +) + +pol.add_section(check_section) + +#### Payment Option - Bank Wire +#### Section 8 + +wire_section = Section( + title="Payment Option - Bank Wire", + html_description="

Complete this section if you wish to be wired the amount to your bank in your local currency. Please fill in as much of the following information as is possible. Please refer to the SFC travel policy for additional bank information required for certain countries.

", + fields={ + "name": {"number":0, "label":"Full name of account holder", "field_type":"string"}, + "address_1": {"number":1, "label":"Street address", "field_type":"string"}, + "address_2": {"number":2, "label":"Street address 2", "field_type":"string"}, + "city": {"number":3, "label":"City", "field_type":"string"}, + "state": {"number":4, "label":"State", "field_type":"string"}, + "zip": {"number":5, "label":"Zip code", "field_type":"string"}, + "account": {"number":6, "label":"Account number", "field_type":"string"}, + "currency": {"number":7, "label":"Preferred currency", "field_type":"string"}, + "bank_name": {"number":8, "label":"Bank name", "field_type":"string"}, + "bank_address": {"number":9, "label":"Bank address", "field_type":"string"}, + "routing_number": {"number":10, "label":"Bank ACH/ABA routing number (US) or SWIFT/BIC code (non-US)", "field_type":"string"}, + "additional_info": {"number":11, "label":"Additional information (see SFC policy)", "field_type":"string"}, + } +) + +pol.add_section(wire_section) +