def test_third_party_expected_for_NEM_reverse_flow_channel(self): """Test that the presence of a reverse flow channel means we are unsure if a service is bundled for a given billing period. """ b = Bill( start=datetime(2019, 2, 28, 0), duration=timedelta(days=27), used_unit="Wh", used=0, _line_items=[], cost=35.51, published=datetime(2017, 2, 1), usage_point="test_usage_point", subscription="test", tariff="B10S", ) reading_type = ReadingType( kind="energy", commodity="electricity SecondaryMetered", unit_of_measure="Wh", flow_direction="reverse", self_url= "https://api.pge.com/GreenButtonConnect/espi/1_1/resource/ReadingType/test='", artifact=self.artifact, ) db.session.add(reading_type) db.session.flush() interval_data = IntervalData( usage_point="test_usage_point", subscription="test", readings=[1] * 96, start=datetime(2019, 2, 28, 0), duration=timedelta(days=1), reading_type_oid=reading_type.oid, artifact=self.artifact, self_url= "https://api.pge.com/GreenButtonConnect/espi/1_1/resource/Subscription/test/UsagePoint/" "test_usage_point/MeterReading/test==/IntervalBlock/test", ) db.session.add(interval_data) actual = b.to_billing_datum(self.meter.utility_service) expected = BillingDatum( start=date(2019, 3, 1), end=date(2019, 3, 27), cost=35.51, used=0, peak=None, items=[], statement=date(2019, 3, 27), attachments=None, utility_code="B10S", utility_account_id=None, utility="utility:pge", service_id=None, third_party_expected=None, ) self.assertEqual(actual, expected)
def test_third_party_expected_for_gas(self): b = Bill( start=datetime(2017, 1, 1, 7), duration=timedelta(days=29), used_unit="therm", used=679.0, _line_items=[], cost=625.64, published=datetime(2017, 2, 1), usage_point="test_usage_point", subscription="test", ) actual = b.to_billing_datum(self.meter.utility_service) expected = BillingDatum( start=date(2017, 1, 2), end=date(2017, 1, 30), cost=625.64, used=679.0, peak=None, items=[], statement=date(2017, 1, 30), attachments=None, utility_code=None, utility_account_id=None, utility="utility:pge", service_id=None, third_party_expected=None, ) self.assertEqual(actual, expected)
def test_third_party_expected_for_NEM_tariff(self): b = Bill( start=datetime(2019, 2, 28, 0), duration=timedelta(days=27), used_unit="Wh", used=0, _line_items=[], cost=35.51, published=datetime(2017, 2, 1), usage_point="test_usage_point", subscription="test", tariff="NEM A-1-B", ) actual = b.to_billing_datum(self.meter.utility_service) expected = BillingDatum( start=date(2019, 3, 1), end=date(2019, 3, 27), cost=35.51, used=0, peak=None, items=[], statement=date(2019, 3, 27), attachments=None, utility_code="NEM A-1-B", utility_account_id=None, utility="utility:pge", service_id=None, third_party_expected=None, ) self.assertEqual(actual, expected)
def test_bill_conversion_gas(self): """An SMD bill can be converted to a BillingDatum, with appropriate unit conversions for gas meters.""" b = Bill( start=datetime(2017, 4, 25, 7), duration=timedelta(days=29), used_unit="therm", used=4400, _line_items=[], cost=4609.48, ) actual = b.to_billing_datum() expected = BillingDatum( start=date(2017, 4, 26), end=date(2017, 5, 24), cost=4609.48, used=4400, peak=None, items=[], statement=date(2017, 5, 24), attachments=None, utility_code="", ) self.assertEqual(expected.start, actual.start) self.assertEqual(expected.end, actual.end) self.assertEqual(expected.cost, actual.cost) self.assertEqual(expected.used, actual.used) self.assertEqual(expected.peak, actual.peak) self.assertEqual(expected.items, actual.items)
def test_overlap(self): """SMD bills can be compared for overlaps based upon their initial and closing dates.""" b0 = Bill(start=datetime(2019, 1, 1, 7), duration=timedelta(days=30)) b1 = Bill(start=datetime(2019, 1, 15, 7), duration=timedelta(days=30)) b2 = Bill( start=datetime(2019, 1, 1, 7) + timedelta(days=30), duration=timedelta(days=30), ) self.assertTrue(b0.overlaps(b0)) self.assertTrue(b0.overlaps(b1)) self.assertTrue(b1.overlaps(b0)) self.assertFalse(b0.overlaps(b2)) self.assertFalse(b2.overlaps(b0))
def _execute(self): config: SmdPartialBillingScraperConfiguration = self._configuration meter = config.meter usage_points = relevant_usage_points(meter) log.info("Identified %s relevant usage point(s): %s", len(usage_points), usage_points) query = db.session.query(SmdBill).filter( SmdBill.usage_point.in_(usage_points)) if self.start_date: start = self.start_date end = max(start, self.end_date or date.today()) if end - self.start_date <= timedelta(days=60): start = start - timedelta(days=60) log.info("Adjusting start date to %s.", start) query = query.filter(start <= SmdBill.start) if self.end_date: query = query.filter(SmdBill.start <= self.end_date) query = query.order_by(SmdBill.published) log.info("Identified %d raw SMD bills relevant to this meter.", query.count()) # It often happens that we receive several versions of the same bill across multiple files. # The first thing we need to do is order the bills by publication date, so we can decide # which SmdBill record is the correct one for our chosen date. unified_bills: List[SmdBill] = SmdBill.unify_bills(query) adjusted_bills: List[SmdBill] = SmdBill.adjust_single_day_bills( unified_bills) partial_bills = [ b.to_billing_datum(self.service) for b in adjusted_bills ] if partial_bills: log.debug( "Identified %s partial bills in Share My Data for meter %s (%s).", len(partial_bills), meter.name, meter.oid, ) if datafeeds_config.enabled("S3_BILL_UPLOAD"): partial_bills = self.attach_corresponding_urja_pdfs( partial_bills) return Results(tnd_bills=partial_bills)
def test_bill_service_config(self): """Test utility account id and service id are pulled from smd_customer_info table and stored on billing datum if corresponding records exist""" usage_point = "test_usage_point" subscription = "test_subscription" service_id = "earlier_service_id" self.add_customer_info("first_service_id", usage_point, subscription, datetime(2017, 1, 1)) # This customer info record should be selected - its published date is just before the new bill's published date self.add_customer_info(service_id, usage_point, subscription, datetime(2017, 4, 1)) self.add_customer_info("third_service_id", usage_point, subscription, datetime(2017, 7, 1)) b = Bill( start=datetime(2017, 4, 25, 7), duration=timedelta(days=29), used_unit="Wh", used=4400, _line_items=[], cost=1344.3, published=datetime(2017, 5, 1), usage_point=usage_point, subscription=subscription, ) actual = b.to_billing_datum(self.meter.utility_service) expected = BillingDatum( start=date(2017, 4, 26), end=date(2017, 5, 24), cost=1344.3, used=4, peak=None, items=[], statement=date(2017, 5, 24), attachments=None, utility_code=None, utility_account_id=None, utility="utility:pge", service_id="earlier_service_id", third_party_expected=False, ) self.assertEqual(actual, expected)
def test_bill_precedence(self): """When bills are selected from SMD tables they are presented in chronological order with no overlaps.""" raw_bills = from_fixture(FIXTURE_01) actual = SmdBill.unify_bills(raw_bills) expected = from_fixture(FIXTURE_02) actual_docs = [b.as_dict() for b in actual] expected_docs = list(reversed([b.as_dict() for b in expected])) self.assertEqual(expected_docs, actual_docs)
def test_bill_published_before_smd_customer_info_record(self): """Test earliest smd customer info record used if bill published before first smd customer info record""" usage_point = "test_usage_point" subscription = "test_subscription" self.add_customer_info("first_service_id", usage_point, subscription, datetime(2018, 5, 5)) self.add_customer_info("updated_service_id", usage_point, subscription, datetime(2019, 1, 1)) b = Bill( start=datetime(2017, 1, 1, 7), duration=timedelta(days=29), used_unit="Wh", used=5400, _line_items=[], cost=3411.2, published=datetime(2017, 2, 1), usage_point=usage_point, subscription=subscription, ) actual = b.to_billing_datum(self.meter.utility_service) expected = BillingDatum( start=date(2017, 1, 2), end=date(2017, 1, 30), cost=3411.2, used=5, peak=None, items=[], statement=date(2017, 1, 30), attachments=None, utility_code=None, utility_account_id=None, utility="utility:pge", service_id="first_service_id", third_party_expected=False, ) self.assertEqual(actual, expected)
def test_one_day_bills(self): """ Test that one-day bills from SMD are adjusted by adding one day to the end date, and adjusting the subsequent bill if necessary. """ raw_bills = from_fixture(FIXTURE_03) actual = SmdBill.unify_bills(raw_bills) adjusted = SmdBill.adjust_single_day_bills(actual) self.assertEqual(len(adjusted), 3) self.assertEqual(adjusted[0].initial, date(2018, 4, 18)) self.assertEqual(adjusted[0].closing, date(2018, 4, 19), "Was previously 4/18/18") self.assertEqual( adjusted[1].initial, date(2018, 4, 20), "Previously 4/19/18, had to be adjusted w/ 4/18 bill.", ) self.assertEqual(adjusted[1].closing, date(2018, 5, 17)) self.assertEqual(adjusted[2].initial, date(2018, 5, 18)) self.assertEqual(adjusted[2].closing, date(2018, 6, 18))
def test_bill_conversion_electric(self): """An SMD bill can be converted to a BillingDatum, with appropriate unit conversions for electric meters.""" b = Bill( start=datetime(2019, 1, 1, 7), duration=timedelta(days=30), used=150000.0, used_unit="Wh", cost=11.0, _line_items=[ { "note": "Customer Charge", "unit": "Wh", "amount": 91.99, "quantity": 20000.0, }, { "note": "Utility Users' Tax", "unit": "Wh", "amount": 148.63, "quantity": 0.0, }, { "note": "Part Peak Energy Charge", "unit": "Wh", "amount": 1302.24, "quantity": 11703443.200000001, }, { "note": "Peak Energy Charge", "unit": "Wh", "amount": 1560.6, "quantity": 10282000.0, }, { "note": "Max Demand Charge", "unit": "W", "amount": 1906.65, "quantity": 157440.0, }, { "note": "Max Peak Demand Charge", "unit": "W", "amount": 1966.33, "quantity": 152960.0, }, { "note": "Off Peak Energy Charge", "unit": "Wh", "amount": 2392.15, "quantity": 28326240.0, }, ], ) actual = b.to_billing_datum() expected = BillingDatum( start=date(2019, 1, 2), end=date(2019, 1, 31), cost=11.0, used=150, peak=157, statement=date(2019, 1, 31), items=[], attachments=None, utility_code="", ) self.assertEqual(expected.start, actual.start) self.assertEqual(expected.end, actual.end) self.assertEqual(expected.cost, actual.cost) self.assertEqual(expected.used, actual.used) self.assertEqual(expected.peak, actual.peak) expected = [ BillingDatumItemsEntry( description="Customer Charge", unit="kWh", total=91.99, quantity=20.0000, rate=None, kind="other", ), BillingDatumItemsEntry( description="Utility Users' Tax", unit="kWh", total=148.63, quantity=0.0, rate=None, kind="other", ), BillingDatumItemsEntry( description="Part Peak Energy Charge", unit="kWh", total=1302.24, quantity=11703.443200000001, rate=None, kind="use", ), BillingDatumItemsEntry( description="Peak Energy Charge", unit="kWh", total=1560.6, quantity=10282.0000, rate=None, kind="use", ), BillingDatumItemsEntry( description="Max Demand Charge", unit="kW", total=1906.65, quantity=157.4400, rate=None, kind="demand", ), BillingDatumItemsEntry( description="Max Peak Demand Charge", unit="kW", total=1966.33, quantity=152.9600, rate=None, kind="demand", ), BillingDatumItemsEntry( description="Off Peak Energy Charge", unit="kWh", total=2392.15, quantity=28326.2400, rate=None, kind="use", ), ] self.assertEqual(expected, actual.items)
def test_initial_closing(self): """An SMD bill can compute its initial and closing dates based on its start and duration.""" b = Bill(start=datetime(2019, 1, 1, 7), duration=timedelta(days=30)) self.assertEqual(date(2019, 1, 2), b.initial) self.assertEqual(date(2019, 1, 31), b.closing)
def test_cca_indicators_in_line_items(self): """Test partial_bill.third_party_expected is set to True if PCIA or generation credits found in PG&E T&D line items """ usage_point = "test_usage_point" subscription = "test_subscription" _line_items = [ { "note": "Generation Credit", "unit": "Wh", "amount": -0.92, "quantity": 0.0, }, { "note": "Franchise Fee Surcharge", "unit": "Wh", "amount": 0.01, "quantity": 0.0, }, { "note": "Peak Energy Charge", "unit": "Wh", "amount": 0.02, "quantity": 80.0, }, { "note": "Part Peak Energy Charge", "unit": "Wh", "amount": 0.06, "quantity": 240.0, }, { "note": "Power Cost Incentive Adjustment", "unit": "Wh", "amount": 0.25, "quantity": 0.0, }, { "note": "Utility Users' Tax", "unit": "Wh", "amount": 1.55, "quantity": 0.0, }, { "note": "Off Peak Energy Charge", "unit": "Wh", "amount": 2.15, "quantity": 9920.0, }, { "note": "Customer Charge", "unit": "Wh", "amount": 19.06, "quantity": 29000.0, }, ] b = Bill( start=datetime(2017, 1, 1, 7), duration=timedelta(days=29), used_unit="Wh", used=5400, _line_items=_line_items, cost=3411.2, published=datetime(2017, 2, 1), usage_point=usage_point, subscription=subscription, ) actual = b.to_billing_datum(self.meter.utility_service) expected_line_items = [ BillingDatumItemsEntry( description="Generation Credit", quantity=0.0, rate=None, total=-0.92, kind="other", unit="kWh", ), BillingDatumItemsEntry( description="Franchise Fee Surcharge", quantity=0.0, rate=None, total=0.01, kind="other", unit="kWh", ), BillingDatumItemsEntry( description="Peak Energy Charge", quantity=0.08, rate=None, total=0.02, kind="use", unit="kWh", ), BillingDatumItemsEntry( description="Part Peak Energy Charge", quantity=0.24, rate=None, total=0.06, kind="use", unit="kWh", ), BillingDatumItemsEntry( description="Power Cost Incentive Adjustment", quantity=0.0, rate=None, total=0.25, kind="other", unit="kWh", ), BillingDatumItemsEntry( description="Utility Users' Tax", quantity=0.0, rate=None, total=1.55, kind="other", unit="kWh", ), BillingDatumItemsEntry( description="Off Peak Energy Charge", quantity=9.92, rate=None, total=2.15, kind="use", unit="kWh", ), BillingDatumItemsEntry( description="Customer Charge", quantity=29.0, rate=None, total=19.06, kind="other", unit="kWh", ), ] expected = BillingDatum( start=date(2017, 1, 2), end=date(2017, 1, 30), cost=3411.2, used=5, peak=None, items=expected_line_items, statement=date(2017, 1, 30), attachments=None, utility_code=None, utility_account_id=None, utility="utility:pge", service_id=None, third_party_expected=True, ) self.assertEqual(actual, expected)