def _consumption_sections(self, case_stub, case_ledgers, section_timestamp_map): case_id = case_stub.case_id for section_id, consumption_section_id in self.stock_settings.section_to_consumption_types.items( ): if section_id in case_ledgers or self.stock_settings.force_consumption_case_filter( case_stub): consumption_entries = [] current_section_sate = case_ledgers.get(section_id, {}) if self.stock_settings.default_product_list: for product_id in self.stock_settings.default_product_list: state = current_section_sate.get(product_id, None) if not state: try: state = LedgerAccessors( self.domain_name).get_ledger_value( case_id, section_id, product_id) except LedgerValueNotFound: pass consumption_entries.append( self._consumption_entry(case_id, product_id, state)) else: for product_id in sorted(current_section_sate.keys()): state = current_section_sate[product_id] consumption_entries.append( self._consumption_entry(case_id, product_id, state)) consumption_entries = [ e for e in consumption_entries if e is not None ] if consumption_entries: yield self.elem_maker.balance( *consumption_entries, **{ 'entity-id': case_id, 'date': section_timestamp_map[section_id], 'section-id': consumption_section_id, })
def test_balance_consumption(self): initial = float(100) initial_amounts = [(p._id, initial) for p in self.products] self.submit_xml_form(balance_submission(initial_amounts)) final_amounts = [(p._id, float(50 - 10*i)) for i, p in enumerate(self.products)] self.submit_xml_form(balance_submission(final_amounts)) for product, amt in final_amounts: self.check_product_stock(self.sp, product, amt, 0) inferred = amt - initial if should_use_sql_backend(self.domain): sql_txn = LedgerAccessors(self.domain.name).get_latest_transaction( self.sp.case_id, 'stock', product ) self.assertEqual(inferred, sql_txn.delta) else: inferred_txn = StockTransaction.objects.get( case_id=self.sp.case_id, product_id=product, subtype=stockconst.TRANSACTION_SUBTYPE_INFERRED ) self.assertEqual(Decimal(str(inferred)), inferred_txn.quantity) self.assertEqual(Decimal(str(amt)), inferred_txn.stock_on_hand) self.assertEqual(stockconst.TRANSACTION_TYPE_CONSUMPTION, inferred_txn.type)
def test_stock_status_data_source_raw(self): ledger_value = LedgerAccessors(self.domain).get_ledger_value( self.supply_point.case_id, "stock", self.product._id ) config = { 'domain': self.domain, 'aggregate': False, 'advanced_columns': True, } self.assertEqual( list(StockStatusDataSource(config).get_data()), [{ 'category': 'nodata', 'consumption': None, 'current_stock': 50, 'last_reported': ledger_value.last_modified, 'location_id': self.location.location_id, 'months_remaining': None, 'product_id': self.product._id, 'product_name': self.product.name, 'resupply_quantity_needed': None}] )
def compute_daily_consumption(domain, case_id, product_id, window_end, section_id=const.SECTION_TYPE_STOCK, configuration=None): """ Computes the consumption for a product at a supply point. Can optionally pass a section_id, but by default the 'stock' value is used for computation. Returns None if there is insufficient history. """ from corehq.form_processor.interfaces.dbaccessors import LedgerAccessors configuration = configuration or ConsumptionConfiguration() window_start = window_end - timedelta(days=configuration.max_window) transactions = LedgerAccessors(domain).get_transactions_for_consumption( case_id, product_id, section_id, window_start, window_end) return compute_daily_consumption_from_transactions(transactions, window_start, configuration)
def test_archive_only_form(self): # check no data in stock states ledger_accessors = LedgerAccessors(self.domain.name) ledger_values = ledger_accessors.get_ledger_values_for_case(self.sp.case_id) self.assertEqual(0, len(ledger_values)) initial_amounts = [(p._id, float(100)) for p in self.products] form_id = self.submit_xml_form(balance_submission(initial_amounts)) # check that we made stuff def _assert_initial_state(): if should_use_sql_backend(self.domain): self.assertEqual(3, LedgerTransaction.objects.filter(form_id=form_id).count()) else: self.assertEqual(1, StockReport.objects.filter(form_id=form_id).count()) self.assertEqual(3, StockTransaction.objects.filter(report__form_id=form_id).count()) ledger_values = ledger_accessors.get_ledger_values_for_case(self.sp.case_id) self.assertEqual(3, len(ledger_values)) for state in ledger_values: self.assertEqual(100, int(state.stock_on_hand)) _assert_initial_state() # archive and confirm commtrack data is cleared form = FormAccessors(self.domain.name).get_form(form_id) form.archive() self.assertEqual(0, len(ledger_accessors.get_ledger_values_for_case(self.sp.case_id))) if should_use_sql_backend(self.domain): self.assertEqual(0, LedgerTransaction.objects.filter(form_id=form_id).count()) else: self.assertEqual(0, StockReport.objects.filter(form_id=form_id).count()) self.assertEqual(0, StockTransaction.objects.filter(report__form_id=form_id).count()) # unarchive and confirm commtrack data is restored form.unarchive() _assert_initial_state()
def setUp(self): super(TestReprocessDuringSubmission, self).setUp() self.factory = CaseFactory(domain=self.domain) self.formdb = XFormInstance.objects self.ledgerdb = LedgerAccessors(self.domain)
def test_archive_last_form(self): initial_amounts = [(p._id, float(100)) for p in self.products] self.submit_xml_form(balance_submission(initial_amounts), timestamp=datetime.utcnow() + timedelta(-30)) final_amounts = [(p._id, float(50)) for i, p in enumerate(self.products)] second_form_id = self.submit_xml_form( balance_submission(final_amounts)) ledger_accessors = LedgerAccessors(self.domain.name) def _assert_initial_state(): if should_use_sql_backend(self.domain): self.assertEqual( 3, LedgerTransaction.objects.filter( form_id=second_form_id).count()) else: self.assertEqual( 1, StockReport.objects.filter(form_id=second_form_id).count()) # 6 = 3 stockonhand and 3 inferred consumption txns self.assertEqual( 6, StockTransaction.objects.filter( report__form_id=second_form_id).count()) ledger_values = ledger_accessors.get_ledger_values_for_case( self.sp.case_id) self.assertEqual(3, len(ledger_values)) for lv in ledger_values: self.assertEqual(50, lv.stock_on_hand) self.assertEqual(round(float(lv.daily_consumption), 2), 1.67) # check initial setup _assert_initial_state() # archive and confirm commtrack data is deleted form = FormAccessors(self.domain.name).get_form(second_form_id) with process_kafka_changes('LedgerToElasticsearchPillow'): form.archive() if should_use_sql_backend(self.domain): self.assertEqual( 0, LedgerTransaction.objects.filter( form_id=second_form_id).count()) else: self.assertEqual( 0, StockReport.objects.filter(form_id=second_form_id).count()) self.assertEqual( 0, StockTransaction.objects.filter( report__form_id=second_form_id).count()) ledger_values = ledger_accessors.get_ledger_values_for_case( self.sp.case_id) self.assertEqual(3, len(ledger_values)) for state in ledger_values: # balance should be reverted to 100 in the StockState self.assertEqual(100, int(state.stock_on_hand)) # consumption should be none since there will only be 1 data point self.assertIsNone(state.daily_consumption) # unarchive and confirm commtrack data is restored with process_kafka_changes('LedgerToElasticsearchPillow'): form.unarchive() _assert_initial_state()
domain=self.domain.name, **submit_extras) return instance_id def check_product_stock(self, case, product_id, expected_soh, expected_qty, section_id='stock'): if not isinstance(expected_qty, Decimal): expected_qty = Decimal(str(expected_qty)) if not isinstance(expected_soh, Decimal): expected_soh = Decimal(str(expected_soh)) latest_trans = LedgerAccessors( self.domain.name).get_latest_transaction(case.case_id, section_id, product_id) self.assertIsNotNone(latest_trans) self.assertEqual(section_id, latest_trans.section_id) self.assertEqual(expected_soh, latest_trans.stock_on_hand) if should_use_sql_backend(self.domain): if latest_trans.type == LedgerTransaction.TYPE_TRANSFER: self.assertEqual(int(expected_qty), latest_trans.delta) else: self.assertEqual(expected_qty, latest_trans.quantity) class CommTrackBalanceTransferTest(CommTrackSubmissionTest): @run_with_all_backends def test_balance_submit(self):
for section in generator.yield_sections(): yield section class StockPayloadGenerator(object): def __init__(self, domain_name, stock_settings, case_stub_list): self.domain_name = domain_name self.stock_settings = stock_settings self.case_stub_list = case_stub_list from lxml.builder import ElementMaker self.elem_maker = ElementMaker(namespace=COMMTRACK_REPORT_XMLNS) def yield_sections(self): case_ids = [case.case_id for case in self.case_stub_list] all_current_ledgers = LedgerAccessors(self.domain_name).get_current_ledger_state(case_ids) for case_stub in self.case_stub_list: case_id = case_stub.case_id case_ledgers = all_current_ledgers[case_id] section_timestamp_map = defaultdict(lambda: json_format_datetime(datetime.utcnow())) for section_id in sorted(case_ledgers.keys()): state_map = case_ledgers[section_id] stock_states = sorted(list(state_map.values()), key=lambda s: s.product_id) as_of = json_format_datetime(max(txn.last_modified_date for txn in stock_states)) section_timestamp_map[section_id] = as_of yield self.elem_maker.balance( *(self._state_to_xml(e) for e in stock_states), **{'entity-id': case_id, 'date': as_of, 'section-id': section_id} )
def test_transactions(expected): state = LedgerAccessors(self.domain).get_current_ledger_state( list(self.case_ids)) for case_id, sections in state.items(): self._validate_case_data(sections, expected[case_id])
def _get_ledger_state(self, case_id): return LedgerAccessors(self.domain_name).get_case_ledger_state(case_id)
def test_get_case_ledger_state(self): for case_id in self.case_ids: state = LedgerAccessors(self.domain).get_case_ledger_state(case_id) for section, products in state.items(): for product, state in products.items(): self.assertEqual(state.stock_on_hand, self.transactions[case_id][section][product])
def setUp(self): super(TestReprocessDuringSubmission, self).setUp() self.factory = CaseFactory(domain=self.domain) self.formdb = FormAccessors(self.domain) self.casedb = CaseAccessors(self.domain) self.ledgerdb = LedgerAccessors(self.domain)
def setUp(self): super(ExplodeLedgersTest, self).setUp() self.case_accessor = CaseAccessors(self.project.name) self.ledger_accessor = LedgerAccessors(self.project.name) self._create_ledgers()
date_formatter=date_formatter, ) submit_form_locally( instance=instance, domain=self.domain.name, **submit_extras ) return instance_id def check_product_stock(self, case, product_id, expected_soh, expected_qty, section_id='stock'): if not isinstance(expected_qty, Decimal): expected_qty = Decimal(str(expected_qty)) if not isinstance(expected_soh, Decimal): expected_soh = Decimal(str(expected_soh)) latest_trans = LedgerAccessors(self.domain.name).get_latest_transaction( case.case_id, section_id, product_id ) self.assertIsNotNone(latest_trans) self.assertEqual(section_id, latest_trans.section_id) self.assertEqual(expected_soh, latest_trans.stock_on_hand) if should_use_sql_backend(self.domain): if latest_trans.type == LedgerTransaction.TYPE_TRANSFER: self.assertEqual(int(expected_qty), latest_trans.delta) else: self.assertEqual(expected_qty, latest_trans.quantity) class CommTrackBalanceTransferTest(CommTrackSubmissionTest):
... } Note: this only works for the Couch backend """ request_params = request.GET case_id = request_params.get('case_id') if not case_id: return json_response( {'message': 'You must specify a case id to make this query.'}, status_code=400) try: case = CaseAccessors(domain).get_case(case_id) except CaseNotFound: raise Http404() ledger_map = LedgerAccessors(domain).get_case_ledger_state(case.case_id) def custom_json_handler(obj): if hasattr(obj, 'stock_on_hand'): return obj.stock_on_hand return json_handler(obj) return json_response( { 'entity_id': case_id, 'ledger': ledger_map, }, default=custom_json_handler, )
def test_transactions(expected): for case_id in self.case_ids: state = LedgerAccessors( self.domain).get_case_ledger_state(case_id) self._validate_case_data(state, expected[case_id])