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(): self.assertEqual( 3, len(self._get_all_ledger_transactions(Q(form_id=form_id)))) 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 = XFormInstance.objects.get_form(form_id, self.domain.name) form.archive() self.assertEqual( 0, len(ledger_accessors.get_ledger_values_for_case(self.sp.case_id))) self.assertEqual( 0, len(self._get_all_ledger_transactions(Q(form_id=form_id)))) # unarchive and confirm commtrack data is restored form.unarchive() _assert_initial_state()
def _assert_stats(self, epxected_tx_count, expected_stock_state_balance, expected_tx_balance): ledger_value = LedgerAccessors(self.domain).get_ledger_value(**self.unique_reference._asdict()) latest_txn = LedgerAccessors(self.domain).get_latest_transaction(**self.unique_reference._asdict()) all_txns = LedgerAccessors(self.domain).get_ledger_transactions_for_case(**self.unique_reference._asdict()) self.assertEqual(epxected_tx_count, len(all_txns)) self.assertEqual(expected_stock_state_balance, ledger_value.stock_on_hand) self.assertEqual(expected_tx_balance, latest_txn.stock_on_hand)
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 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, len(self._get_all_ledger_transactions(Q(form_id=second_form_id)))) 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_pillow_changes('LedgerToElasticsearchPillow'): form.archive() if should_use_sql_backend(self.domain): self.assertEqual(0, len(self._get_all_ledger_transactions(Q(form_id=second_form_id)))) 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_pillow_changes('LedgerToElasticsearchPillow'): form.unarchive() _assert_initial_state()
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 get_episode_adherence_ledger(domain, episode_case_id, entry_id): """ :param domain: domain name :param episode_case_id: episode case id for adherence :param entry_id: example date_2017-12-09 """ ledger_accessor = LedgerAccessors(domain) try: return ledger_accessor.get_ledger_value(episode_case_id, EPISODE_LEDGER_SECTION_ID, entry_id) except LedgerValueNotFound: return None
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(): self.assertEqual( 3, len( self._get_all_ledger_transactions( Q(form_id=second_form_id)))) 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 = XFormInstance.objects.get_form(second_form_id, self.domain.name) with self.process_legder_changes: form.archive() self.assertEqual( 0, len(self._get_all_ledger_transactions(Q(form_id=second_form_id)))) 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 ledger value 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 self.process_legder_changes: form.unarchive() _assert_initial_state()
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 get_wrapped_ledger_values(domain, case_ids, section_id, entry_ids=None): if isinstance(case_ids, (list, tuple, set)): case_ids_list = list(case_ids) else: case_ids_list = [case_ids] return LedgerAccessors(domain).get_ledger_values_for_cases( case_ids_list, [section_id], entry_ids)
def test_get_case_ledger_state_1(self): 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]) self._test_get_current_ledger_transactions(test_transactions) self.assertEqual({}, LedgerAccessors(self.domain).get_case_ledger_state('non-existent'))
def test_get_current_ledger_state(self): def test_transactions(expected): state = LedgerAccessors(self.domain).get_current_ledger_state(self.case_ids.keys()) for case_id, sections in state.items(): self._validate_case_data(sections, expected[case_id]) self._test_get_current_ledger_transactions(test_transactions) self.assertEqual({}, LedgerAccessors(self.domain).get_current_ledger_state([]))
def _get_values_by_product(ledger_section, case_id, product_codes, domain): """returns a defaultdict mapping product codes to their values""" ret = defaultdict(lambda: 0) ledgers = LedgerAccessors(domain).get_ledger_values_for_case(case_id) for ledger in ledgers: if ledger.section_id == ledger_section and ledger.entry_id in product_codes: ret[ledger.entry_id] = ledger.stock_on_hand return ret
class ExplodeLedgersTest(BaseSyncTest): def setUp(self): super(ExplodeLedgersTest, self).setUp() self.case_accessor = CaseAccessors(self.project.name) self.ledger_accessor = LedgerAccessors(self.project.name) self._create_ledgers() def tearDown(self): delete_all_ledgers() delete_all_cases() delete_all_xforms() super(ExplodeLedgersTest, self).tearDown() def _create_ledgers(self): case_type = 'case' case1 = CaseStructure( case_id='case1', attrs={'create': True, 'case_type': case_type}, ) case2 = CaseStructure( case_id='case2', attrs={'create': True, 'case_type': case_type}, ) # case2 will have no ledgers self.ledgers = { 'icecream': Balance( entity_id=case1.case_id, date=datetime(2017, 11, 21, 0, 0, 0, 0), section_id='test', entry=Entry(id='icecream', quantity=4), ), 'blondie': Balance( entity_id=case1.case_id, date=datetime(2017, 11, 21, 0, 0, 0, 0), section_id='test', entry=Entry(id='blondie', quantity=5), ) } self.device.post_changes([case1, case2]) self.device.post_changes(list(self.ledgers.values())) def test_explode_ledgers(self): explode_cases(self.project.name, self.user_id, 5) cases = self.case_accessor.iter_cases(self.case_accessor.get_case_ids_in_domain()) for case in cases: ledger_values = {l.entry_id: l for l in self.ledger_accessor.get_ledger_values_for_case(case.case_id)} if case.case_id == 'case2' or case.get_case_property('cc_exploded_from') == 'case2': self.assertEqual(len(ledger_values), 0) else: self.assertEqual(len(ledger_values), len(self.ledgers)) for id, balance in six.iteritems(self.ledgers): self.assertEqual(ledger_values[id].balance, balance.entry.quantity) self.assertEqual(ledger_values[id].entry_id, balance.entry.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 sql_txn = LedgerAccessors(self.domain.name).get_latest_transaction( self.sp.case_id, 'stock', product) self.assertEqual(inferred, sql_txn.delta)
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.name): 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.name): 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 rebuild_case_changes(form, rebuild_reason=None): """ Publishes changes for the form and rebuilds any touched cases. """ domain = form.domain case_ids = get_case_ids_from_form(form) for case_id in case_ids: detail = FormReprocessRebuild(form_id=form.form_id) FormProcessorInterface(domain).hard_rebuild_case(case_id, detail) if LedgerAccessors(domain).get_ledger_values_for_case(case_id): with open('case_ids_with_ledgers.csv', 'a+') as f: print("{}, {}".format(domain, case_id), file=f) return len(case_ids)
def get_values_by_product(domain, case_id, ledger_section, product_codes): ledgers = LedgerAccessors(domain).get_ledger_values_for_case(case_id) products = SQLProduct.objects.filter(domain=domain, code__in=product_codes).values( 'product_id', 'code') entry_id_to_code = { product['product_id']: product['code'] for product in products } return { entry_id_to_code[ledger.entry_id]: ledger.stock_on_hand for ledger in ledgers if (ledger.section_id == ledger_section and ledger.entry_id in entry_id_to_code) }
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_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 setUp(self): super(ExplodeLedgersTest, self).setUp() self.case_accessor = CaseAccessors(self.project.name) self.ledger_accessor = LedgerAccessors(self.project.name) self._create_ledgers()
class ReprocessSubmissionStubTests(TestCase): @classmethod def setUpClass(cls): super(ReprocessSubmissionStubTests, cls).setUpClass() cls.domain = uuid.uuid4().hex cls.product = SQLProduct.objects.create(domain=cls.domain, product_id='product1', name='product1') @classmethod def tearDownClass(cls): cls.product.delete() super(ReprocessSubmissionStubTests, cls).tearDownClass() def setUp(self): super(ReprocessSubmissionStubTests, self).setUp() self.factory = CaseFactory(domain=self.domain) self.formdb = XFormInstance.objects self.ledgerdb = LedgerAccessors(self.domain) def tearDown(self): FormProcessorTestUtils.delete_all_cases_forms_ledgers(self.domain) super(ReprocessSubmissionStubTests, self).tearDown() def test_reprocess_unfinished_submission_case_create(self): case_id = uuid.uuid4().hex with _patch_save_to_raise_error(self): self.factory.create_or_update_cases([ CaseStructure(case_id=case_id, attrs={ 'case_type': 'parent', 'create': True }) ]) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) # form that was saved before case error raised normal_form_ids = XFormInstance.objects.get_form_ids_in_domain( self.domain, 'XFormInstance') self.assertEqual(0, len(normal_form_ids)) # shows error form (duplicate of form that was saved before case error) # this is saved becuase the saving was assumed to be atomic so if there was any error it's assumed # the form didn't get saved # we don't really care about this form in this test error_forms = XFormInstance.objects.get_forms_by_type( self.domain, 'XFormError', 10) self.assertEqual(1, len(error_forms)) self.assertIsNone(error_forms[0].orig_id) self.assertEqual(error_forms[0].form_id, stubs[0].xform_id) self.assertEqual( 0, len(CommCareCase.objects.get_case_ids_in_domain(self.domain))) result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) case_ids = CommCareCase.objects.get_case_ids_in_domain(self.domain) self.assertEqual(1, len(case_ids)) self.assertEqual(case_id, case_ids[0]) with self.assertRaises(UnfinishedSubmissionStub.DoesNotExist): UnfinishedSubmissionStub.objects.get(pk=stubs[0].pk) def test_reprocess_unfinished_submission_case_update(self): case_id = uuid.uuid4().hex form_ids = [] form_ids.append( submit_case_blocks( CaseBlock(case_id=case_id, create=True, case_type='box').as_text(), self.domain)[0].form_id) with _patch_save_to_raise_error(self): submit_case_blocks( CaseBlock(case_id=case_id, update={ 'prop': 'a' }).as_text(), self.domain) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) form_ids.append(stubs[0].xform_id) # submit second form with case update form_ids.append( submit_case_blocks( CaseBlock(case_id=case_id, update={ 'prop': 'b' }).as_text(), self.domain)[0].form_id) case = CommCareCase.objects.get_case(case_id, self.domain) self.assertEqual(2, len(case.xform_ids)) self.assertEqual('b', case.get_case_property('prop')) result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) self.assertEqual(0, len(result.ledgers)) case = CommCareCase.objects.get_case(case_id, self.domain) self.assertEqual('b', case.get_case_property( 'prop')) # should be property value from most recent form self.assertEqual(3, len(case.xform_ids)) self.assertEqual(form_ids, case.xform_ids) with self.assertRaises(UnfinishedSubmissionStub.DoesNotExist): UnfinishedSubmissionStub.objects.get(pk=stubs[0].pk) def test_reprocess_unfinished_submission_ledger_create(self): from corehq.apps.commtrack.tests.util import get_single_balance_block case_id = uuid.uuid4().hex self.factory.create_or_update_cases([ CaseStructure(case_id=case_id, attrs={ 'case_type': 'parent', 'create': True }) ]) with _patch_save_to_raise_error(self): submit_case_blocks( get_single_balance_block(case_id, 'product1', 100), self.domain) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) ledgers = self.ledgerdb.get_ledger_values_for_case(case_id) self.assertEqual(0, len(ledgers)) case = CommCareCase.objects.get_case(case_id, self.domain) self.assertEqual(1, len(case.xform_ids)) ledger_transactions = self.ledgerdb.get_ledger_transactions_for_case( case_id) self.assertEqual(0, len(ledger_transactions)) result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) self.assertEqual(1, len(result.ledgers)) ledgers = self.ledgerdb.get_ledger_values_for_case(case_id) self.assertEqual(1, len(ledgers)) ledger_transactions = self.ledgerdb.get_ledger_transactions_for_case( case_id) self.assertEqual(1, len(ledger_transactions)) # case still only has 2 transactions case = CommCareCase.objects.get_case(case_id, self.domain) self.assertEqual(2, len(case.xform_ids)) self.assertTrue(case.actions[1].is_ledger_transaction) def test_reprocess_unfinished_submission_ledger_rebuild(self): from corehq.apps.commtrack.tests.util import get_single_balance_block case_id = uuid.uuid4().hex form_ids = [] form_ids.append( submit_case_blocks([ CaseBlock(case_id=case_id, create=True, case_type='shop').as_text(), get_single_balance_block(case_id, 'product1', 100), ], self.domain)[0].form_id) with _patch_save_to_raise_error(self): submit_case_blocks( get_single_balance_block(case_id, 'product1', 50), self.domain) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) form_ids.append(stubs[0].xform_id) # submit another form afterwards form_ids.append( submit_case_blocks( get_single_balance_block(case_id, 'product1', 25), self.domain)[0].form_id) ledgers = self.ledgerdb.get_ledger_values_for_case(case_id) self.assertEqual(1, len(ledgers)) self.assertEqual(25, ledgers[0].balance) ledger_transactions = self.ledgerdb.get_ledger_transactions_for_case( case_id) self.assertEqual(2, len(ledger_transactions)) # should rebuild ledger transactions result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) self.assertEqual(1, len(result.ledgers)) ledgers = self.ledgerdb.get_ledger_values_for_case(case_id) self.assertEqual(1, len(ledgers)) # still only 1 self.assertEqual(25, ledgers[0].balance) ledger_transactions = self.ledgerdb.get_ledger_transactions_for_case( case_id) self.assertEqual(3, len(ledger_transactions)) # make sure transactions are in correct order self.assertEqual(form_ids, [trans.form_id for trans in ledger_transactions]) self.assertEqual(100, ledger_transactions[0].updated_balance) self.assertEqual(100, ledger_transactions[0].delta) self.assertEqual(50, ledger_transactions[1].updated_balance) self.assertEqual(-50, ledger_transactions[1].delta) self.assertEqual(25, ledger_transactions[2].updated_balance) self.assertEqual(-25, ledger_transactions[2].delta) def test_fire_signals(self): from corehq.apps.receiverwrapper.tests.test_submit_errors import failing_signal_handler case_id = uuid.uuid4().hex form_id = uuid.uuid4().hex with failing_signal_handler('signal death'): submit_case_blocks(CaseBlock(case_id=case_id, create=True, case_type='box').as_text(), self.domain, form_id=form_id) form = self.formdb.get_form(form_id) with catch_signal(successful_form_received) as form_handler, \ catch_signal(sql_case_post_save) as case_handler: submit_form_locally( instance=form.get_xml(), domain=self.domain, ) case = CommCareCase.objects.get_case(case_id, self.domain) self.assertEqual(form, form_handler.call_args[1]['xform']) self.assertEqual(case, case_handler.call_args[1]['case']) def test_reprocess_normal_form(self): case_id = uuid.uuid4().hex form, cases = submit_case_blocks( CaseBlock(case_id=case_id, create=True, case_type='box').as_text(), self.domain) self.assertTrue(form.is_normal) result = reprocess_form(form, save=True, lock_form=False) self.assertIsNone(result.error) case = CommCareCase.objects.get_case(case_id, self.domain) transactions = case.actions self.assertEqual([trans.form_id for trans in transactions], [form.form_id]) def test_processing_skipped_when_migrations_are_in_progress(self): case_id = uuid.uuid4().hex with _patch_save_to_raise_error(self): self.factory.create_or_update_cases([ CaseStructure(case_id=case_id, attrs={ 'case_type': 'parent', 'create': True }) ]) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) with patch( 'corehq.form_processor.reprocess.any_migrations_in_progress', return_value=True): result = reprocess_unfinished_stub(stubs[0]) self.assertIsNone(result) result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) def test_processing_retuns_error_for_missing_form(self): case_id = uuid.uuid4().hex with _patch_save_to_raise_error(self): self.factory.create_or_update_cases([ CaseStructure(case_id=case_id, attrs={ 'case_type': 'parent', 'create': True }) ]) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) FormProcessorTestUtils.delete_all_cases_forms_ledgers(self.domain) with self.assertRaises(XFormNotFound): self.formdb.get_form(stubs[0].xform_id) result = reprocess_unfinished_stub(stubs[0]) self.assertIsNotNone(result.error)
def setUp(self): super(TestReprocessDuringSubmission, self).setUp() self.factory = CaseFactory(domain=self.domain) self.formdb = XFormInstance.objects self.ledgerdb = LedgerAccessors(self.domain)
class ReprocessSubmissionStubTests(TestCase): @classmethod def setUpClass(cls): super(ReprocessSubmissionStubTests, cls).setUpClass() cls.domain = uuid.uuid4().hex cls.product = SQLProduct.objects.create(domain=cls.domain, product_id='product1', name='product1') @classmethod def tearDownClass(cls): cls.product.delete() super(ReprocessSubmissionStubTests, cls).tearDownClass() def setUp(self): super(ReprocessSubmissionStubTests, self).setUp() self.factory = CaseFactory(domain=self.domain) self.formdb = FormAccessors(self.domain) self.casedb = CaseAccessors(self.domain) self.ledgerdb = LedgerAccessors(self.domain) def tearDown(self): FormProcessorTestUtils.delete_all_cases_forms_ledgers(self.domain) super(ReprocessSubmissionStubTests, self).tearDown() def test_reprocess_unfinished_submission_case_create(self): case_id = uuid.uuid4().hex with _patch_save_to_raise_error(self): self.factory.create_or_update_cases([ CaseStructure(case_id=case_id, attrs={'case_type': 'parent', 'create': True}) ]) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) # form that was saved before case error raised normal_form_ids = self.formdb.get_all_form_ids_in_domain('XFormInstance') self.assertEqual(0, len(normal_form_ids)) # shows error form (duplicate of form that was saved before case error) # this is saved becuase the saving was assumed to be atomic so if there was any error it's assumed # the form didn't get saved # we don't really care about this form in this test error_forms = self.formdb.get_forms_by_type('XFormError', 10) self.assertEqual(1, len(error_forms)) self.assertIsNone(error_forms[0].orig_id) self.assertEqual(error_forms[0].form_id, stubs[0].xform_id) self.assertEqual(0, len(self.casedb.get_case_ids_in_domain(self.domain))) result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) case_ids = self.casedb.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) self.assertEqual(case_id, case_ids[0]) with self.assertRaises(UnfinishedSubmissionStub.DoesNotExist): UnfinishedSubmissionStub.objects.get(pk=stubs[0].pk) def test_reprocess_unfinished_submission_case_update(self): case_id = uuid.uuid4().hex form_ids = [] form_ids.append(submit_case_blocks( CaseBlock(case_id=case_id, create=True, case_type='box').as_string().decode('utf-8'), self.domain )[0].form_id) with _patch_save_to_raise_error(self): submit_case_blocks( CaseBlock(case_id=case_id, update={'prop': 'a'}).as_string().decode('utf-8'), self.domain ) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) form_ids.append(stubs[0].xform_id) # submit second form with case update form_ids.append(submit_case_blocks( CaseBlock(case_id=case_id, update={'prop': 'b'}).as_string().decode('utf-8'), self.domain )[0].form_id) case = self.casedb.get_case(case_id) self.assertEqual(2, len(case.xform_ids)) self.assertEqual('b', case.get_case_property('prop')) result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) self.assertEqual(0, len(result.ledgers)) case = self.casedb.get_case(case_id) self.assertEqual('b', case.get_case_property('prop')) # should be property value from most recent form self.assertEqual(3, len(case.xform_ids)) self.assertEqual(form_ids, case.xform_ids) with self.assertRaises(UnfinishedSubmissionStub.DoesNotExist): UnfinishedSubmissionStub.objects.get(pk=stubs[0].pk) def test_reprocess_unfinished_submission_ledger_create(self): from corehq.apps.commtrack.tests.util import get_single_balance_block case_id = uuid.uuid4().hex self.factory.create_or_update_cases([ CaseStructure(case_id=case_id, attrs={'case_type': 'parent', 'create': True}) ]) with _patch_save_to_raise_error(self): submit_case_blocks( get_single_balance_block(case_id, 'product1', 100), self.domain ) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) ledgers = self.ledgerdb.get_ledger_values_for_case(case_id) self.assertEqual(0, len(ledgers)) case = self.casedb.get_case(case_id) self.assertEqual(1, len(case.xform_ids)) ledger_transactions = self.ledgerdb.get_ledger_transactions_for_case(case_id) self.assertEqual(0, len(ledger_transactions)) result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) self.assertEqual(1, len(result.ledgers)) ledgers = self.ledgerdb.get_ledger_values_for_case(case_id) self.assertEqual(1, len(ledgers)) ledger_transactions = self.ledgerdb.get_ledger_transactions_for_case(case_id) self.assertEqual(1, len(ledger_transactions)) # case still only has 2 transactions case = self.casedb.get_case(case_id) self.assertEqual(2, len(case.xform_ids)) if should_use_sql_backend(self.domain): self.assertTrue(case.actions[1].is_ledger_transaction) def test_reprocess_unfinished_submission_ledger_rebuild(self): from corehq.apps.commtrack.tests.util import get_single_balance_block case_id = uuid.uuid4().hex form_ids = [] form_ids.append(submit_case_blocks( [ CaseBlock(case_id=case_id, create=True, case_type='shop').as_string().decode('utf-8'), get_single_balance_block(case_id, 'product1', 100), ], self.domain )[0].form_id) with _patch_save_to_raise_error(self): submit_case_blocks( get_single_balance_block(case_id, 'product1', 50), self.domain ) stubs = UnfinishedSubmissionStub.objects.filter(domain=self.domain, saved=False).all() self.assertEqual(1, len(stubs)) form_ids.append(stubs[0].xform_id) # submit another form afterwards form_ids.append(submit_case_blocks( get_single_balance_block(case_id, 'product1', 25), self.domain )[0].form_id) ledgers = self.ledgerdb.get_ledger_values_for_case(case_id) self.assertEqual(1, len(ledgers)) self.assertEqual(25, ledgers[0].balance) ledger_transactions = self.ledgerdb.get_ledger_transactions_for_case(case_id) if should_use_sql_backend(self.domain): self.assertEqual(2, len(ledger_transactions)) else: # includes extra consumption transaction self.assertEqual(3, len(ledger_transactions)) # should rebuild ledger transactions result = reprocess_unfinished_stub(stubs[0]) self.assertEqual(1, len(result.cases)) self.assertEqual(1, len(result.ledgers)) ledgers = self.ledgerdb.get_ledger_values_for_case(case_id) self.assertEqual(1, len(ledgers)) # still only 1 self.assertEqual(25, ledgers[0].balance) ledger_transactions = self.ledgerdb.get_ledger_transactions_for_case(case_id) if should_use_sql_backend(self.domain): self.assertEqual(3, len(ledger_transactions)) # make sure transactions are in correct order self.assertEqual(form_ids, [trans.form_id for trans in ledger_transactions]) self.assertEqual(100, ledger_transactions[0].updated_balance) self.assertEqual(100, ledger_transactions[0].delta) self.assertEqual(50, ledger_transactions[1].updated_balance) self.assertEqual(-50, ledger_transactions[1].delta) self.assertEqual(25, ledger_transactions[2].updated_balance) self.assertEqual(-25, ledger_transactions[2].delta) else: self.assertEqual(3, len(ledger_transactions)) self.assertEqual(form_ids, [trans.report.form_id for trans in ledger_transactions]) self.assertEqual(100, ledger_transactions[0].stock_on_hand) self.assertEqual(50, ledger_transactions[1].stock_on_hand) self.assertEqual(25, ledger_transactions[2].stock_on_hand) def test_fire_signals(self): from corehq.apps.receiverwrapper.tests.test_submit_errors import failing_signal_handler case_id = uuid.uuid4().hex form_id = uuid.uuid4().hex with failing_signal_handler('signal death'): submit_case_blocks( CaseBlock(case_id=case_id, create=True, case_type='box').as_string().decode('utf-8'), self.domain, form_id=form_id ) form = self.formdb.get_form(form_id) with catch_signal(successful_form_received) as form_handler, catch_signal(case_post_save) as case_handler: submit_form_locally( instance=form.get_xml(), domain=self.domain, ) case = self.casedb.get_case(case_id) if should_use_sql_backend(self.domain): self.assertEqual(form, form_handler.call_args[1]['xform']) self.assertEqual(case, case_handler.call_args[1]['case']) else: signal_form = form_handler.call_args[1]['xform'] self.assertEqual(form.form_id, signal_form.form_id) self.assertEqual(form.get_rev, signal_form.get_rev) signal_case = case_handler.call_args[1]['case'] self.assertEqual(case.case_id, signal_case.case_id) self.assertEqual(case.get_rev, signal_case.get_rev)
def setUp(self): super(ReprocessSubmissionStubTests, self).setUp() self.factory = CaseFactory(domain=self.domain) self.formdb = FormAccessors(self.domain) self.casedb = CaseAccessors(self.domain) self.ledgerdb = LedgerAccessors(self.domain)
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 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])
def render_case(case, options): """ Uses options since Django 1.3 doesn't seem to support templatetag kwargs. Change to kwargs when we're on a version of Django that does. """ from corehq.apps.hqwebapp.templatetags.proptable_tags import get_tables_as_rows, get_default_definition wrapped_case = get_wrapped_case(case) timezone = options.get('timezone', pytz.utc) timezone = timezone.localize(datetime.datetime.utcnow()).tzinfo _get_tables_as_rows = partial(get_tables_as_rows, timezone=timezone) display = options.get('display') or wrapped_case.get_display_config() show_transaction_export = options.get('show_transaction_export') or False get_case_url = options['get_case_url'] data = copy.deepcopy(wrapped_case.to_full_dict()) default_properties = _get_tables_as_rows(data, display) dynamic_data = wrapped_case.dynamic_properties() for section in display: for row in section['layout']: for item in row: dynamic_data.pop(item.get("expr"), None) if dynamic_data: dynamic_keys = sorted(dynamic_data.keys()) definition = get_default_definition( dynamic_keys, num_columns=DYNAMIC_CASE_PROPERTIES_COLUMNS) dynamic_properties = _get_tables_as_rows(dynamic_data, definition) else: dynamic_properties = None the_time_is_now = datetime.datetime.utcnow() tz_offset_ms = int(timezone.utcoffset(the_time_is_now).total_seconds()) * 1000 tz_abbrev = timezone.localize(the_time_is_now).tzname() # ledgers def _product_name(product_id): try: return SQLProduct.objects.get(product_id=product_id).name except SQLProduct.DoesNotExist: return (_('Unknown Product ("{}")').format(product_id)) ledger_map = LedgerAccessors(case.domain).get_case_ledger_state(case.case_id, ensure_form_id=True) for section, product_map in ledger_map.items(): product_tuples = sorted( (_product_name(product_id), product_map[product_id]) for product_id in product_map ) ledger_map[section] = product_tuples return render_to_string("case/partials/single_case.html", { "default_properties": default_properties, "default_properties_options": { "style": "table" }, "dynamic_properties": dynamic_properties, "dynamic_properties_options": { "style": "table" }, "case": wrapped_case.case, "case_actions": mark_safe(json.dumps(wrapped_case.actions())), "timezone": timezone, "tz_abbrev": tz_abbrev, "case_hierarchy_options": { "show_view_buttons": True, "get_case_url": get_case_url, "timezone": timezone }, "ledgers": ledger_map, "timezone_offset": tz_offset_ms, "show_transaction_export": show_transaction_export, "xform_api_url": reverse('single_case_forms', args=[case.domain, case.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 test_transactions(expected): state = LedgerAccessors(self.domain).get_current_ledger_state(self.case_ids.keys()) 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)
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 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)