def safe_hard_delete(case): """ Hard delete a case - by deleting the case itself as well as all forms associated with it permanently from the database. Will fail hard if the case has any reverse indices or if any of the forms associated with the case also touch other cases. This is used primarily for cleaning up system cases/actions (e.g. the location delegate case). """ if not settings.UNIT_TESTING: from corehq.apps.commtrack.const import USER_LOCATION_OWNER_MAP_TYPE if not (case.is_deleted or case.type == USER_LOCATION_OWNER_MAP_TYPE): raise CommCareCaseError("Attempt to hard delete a live case whose type isn't white listed") if case.reverse_indices: raise CommCareCaseError("You can't hard delete a case that has other dependencies ({})!".format(case.case_id)) interface = FormProcessorInterface(case.domain) forms = interface.get_case_forms(case.case_id) for form in forms: case_updates = get_case_updates(form) if any([c.id != case.case_id for c in case_updates]): raise CommCareCaseError("You can't hard delete a case that has shared forms with other cases!") interface.hard_delete_case_and_forms(case, forms)
class LedgerTests(TestCase): @classmethod def setUpClass(cls): FormProcessorTestUtils.delete_all_cases(DOMAIN) FormProcessorTestUtils.delete_all_xforms(DOMAIN) cls.product = make_product(DOMAIN, 'A Product', 'prodcode') def setUp(self): self.interface = FormProcessorInterface(domain=DOMAIN) self.factory = CaseFactory(domain=DOMAIN) self.case = self.factory.create_case() def _submit_ledgers(self, ledger_block, quantity): return submit_case_blocks( ledger_block.format(case_id=self.case.case_id, product_id=self.product._id, quantity=quantity), DOMAIN ) def _set_balance(self, balance): self._submit_ledgers(BALANCE_BLOCK, balance) @run_with_all_backends def test_balance_submission(self): orignal_form_count = len(self.interface.get_case_forms(self.case.case_id)) self._submit_ledgers(BALANCE_BLOCK, 100) self._assert_ledger_state(100) # make sure the form is part of the case's history self.assertEqual(orignal_form_count + 1, len(self.interface.get_case_forms(self.case.case_id))) @run_with_all_backends def test_balance_submission_with_prior_balance(self): self._set_balance(100) self._assert_ledger_state(100) self._set_balance(50) self._assert_ledger_state(50) self._set_balance(150) self._assert_ledger_state(150) @run_with_all_backends def test_transfer_submission(self): orignal_form_count = len(self.interface.get_case_forms(self.case.case_id)) self._submit_ledgers(TRANSFER_BLOCK, 100) self._assert_ledger_state(100) # make sure the form is part of the case's history self.assertEqual(orignal_form_count + 1, len(self.interface.get_case_forms(self.case.case_id))) @run_with_all_backends def test_transfer_submission_with_prior_balance(self): self._set_balance(100) self._submit_ledgers(TRANSFER_BLOCK, 100) self._assert_ledger_state(200) def _assert_ledger_state(self, expected_balance): ledgers = self.interface.ledger_processor.get_ledgers_for_case(self.case.case_id) self.assertEqual(1, len(ledgers)) ledger = ledgers[0] self.assertEqual(self.case.case_id, ledger.case_id) self.assertEqual(self.product._id, ledger.entry_id) self.assertEqual('stock', ledger.section_id) self.assertEqual(expected_balance, ledger.balance)
class LedgerTests(TestCase): @classmethod def setUpClass(cls): super(LedgerTests, cls).setUpClass() cls.product_a = make_product(DOMAIN, 'A Product', 'prodcode_a') cls.product_b = make_product(DOMAIN, 'B Product', 'prodcode_b') cls.product_c = make_product(DOMAIN, 'C Product', 'prodcode_c') @classmethod def tearDownClass(cls): cls.product_a.delete() cls.product_b.delete() cls.product_c.delete() FormProcessorTestUtils.delete_all_cases_forms_ledgers(DOMAIN) with override_settings(TESTS_SHOULD_USE_SQL_BACKEND=True): FormProcessorTestUtils.delete_all_cases_forms_ledgers(DOMAIN) super(LedgerTests, cls).tearDownClass() def setUp(self): super(LedgerTests, self).setUp() self.interface = FormProcessorInterface(domain=DOMAIN) self.factory = CaseFactory(domain=DOMAIN) self.case = self.factory.create_case() def _submit_ledgers(self, ledger_blocks): return submit_case_blocks(ledger_blocks, DOMAIN)[0].form_id def _set_balance(self, balance): from corehq.apps.commtrack.tests.util import get_single_balance_block self._submit_ledgers([ get_single_balance_block(self.case.case_id, self.product_a._id, balance) ]) def _transfer_in(self, amount): from corehq.apps.commtrack.tests.util import get_single_transfer_block self._submit_ledgers([ get_single_transfer_block(None, self.case.case_id, self.product_a._id, amount) ]) def _transfer_out(self, amount): from corehq.apps.commtrack.tests.util import get_single_transfer_block self._submit_ledgers([ get_single_transfer_block(self.case.case_id, None, self.product_a._id, amount) ]) @run_with_all_backends def test_balance_submission(self): orignal_form_count = len(self.interface.get_case_forms(self.case.case_id)) self._set_balance(100) self._assert_ledger_state(100) # make sure the form is part of the case's history self.assertEqual(orignal_form_count + 1, len(self.interface.get_case_forms(self.case.case_id))) self._assert_transactions([ self._expected_val(100, 100) ]) @run_with_all_backends def test_balance_submission_multiple(self): from corehq.apps.commtrack.tests.util import get_single_balance_block balances = { self.product_a._id: 100, self.product_b._id: 50, self.product_c._id: 25, } self._submit_ledgers([ get_single_balance_block(self.case.case_id, prod_id, balance) for prod_id, balance in balances.items() ]) expected_transactions = [] for prod_id, expected_balance in balances.items(): expected_transactions.append(self._expected_val( expected_balance, expected_balance, product_id=prod_id )) balance = self.interface.ledger_db.get_current_ledger_value( UniqueLedgerReference( case_id=self.case.case_id, section_id='stock', entry_id=prod_id )) self.assertEqual(expected_balance, balance) self._assert_transactions(expected_transactions, ignore_ordering=True) @run_with_all_backends def test_balance_submission_with_prior_balance(self): self._set_balance(100) self._assert_ledger_state(100) self._set_balance(50) self._assert_ledger_state(50) self._set_balance(150) self._assert_ledger_state(150) self._assert_transactions([ self._expected_val(100, 100), self._expected_val(-50, 50), self._expected_val(100, 150), ]) @run_with_all_backends def test_transfer_submission(self): orignal_form_count = len(self.interface.get_case_forms(self.case.case_id)) self._transfer_in(100) self._assert_ledger_state(100) # make sure the form is part of the case's history self.assertEqual(orignal_form_count + 1, len(self.interface.get_case_forms(self.case.case_id))) self._assert_transactions([ self._expected_val(100, 100, type_=LedgerTransaction.TYPE_TRANSFER), ]) @run_with_all_backends def test_transfer_submission_with_prior_balance(self): self._set_balance(100) self._transfer_in(100) self._assert_ledger_state(200) self._assert_transactions([ self._expected_val(100, 100), self._expected_val(100, 200, type_=LedgerTransaction.TYPE_TRANSFER), ]) @run_with_all_backends def test_full_combination(self): self._set_balance(100) self._transfer_in(100) self._assert_ledger_state(200) self._transfer_out(20) self._assert_ledger_state(180) self._set_balance(150) self._set_balance(170) self._transfer_out(30) self._assert_ledger_state(140) self._assert_transactions([ self._expected_val(100, 100), self._expected_val(100, 200, type_=LedgerTransaction.TYPE_TRANSFER), self._expected_val(-20, 180, type_=LedgerTransaction.TYPE_TRANSFER), self._expected_val(-30, 150), self._expected_val(20, 170), self._expected_val(-30, 140, type_=LedgerTransaction.TYPE_TRANSFER), ]) @run_with_all_backends def test_ledger_update_with_case_update(self): from corehq.apps.commtrack.tests.util import get_single_balance_block submit_case_blocks([ CaseBlock(case_id=self.case.case_id, update={'a': "1"}).as_string(), get_single_balance_block(self.case.case_id, self.product_a._id, 100)], DOMAIN ) self._assert_ledger_state(100) case = CaseAccessors(DOMAIN).get_case(self.case.case_id) self.assertEqual("1", case.dynamic_case_properties()['a']) if settings.TESTS_SHOULD_USE_SQL_BACKEND: transactions = CaseAccessorSQL.get_transactions(self.case.case_id) self.assertEqual(2, len(transactions)) self.assertTrue(transactions[0].is_form_transaction) # ordering not guaranteed since they have the same date self.assertTrue(transactions[1].is_form_transaction) self.assertTrue(transactions[1].is_ledger_transaction) self._assert_transactions([ self._expected_val(100, 100), ]) def _assert_ledger_state(self, expected_balance): ledgers = self.interface.ledger_db.get_ledgers_for_case(self.case.case_id) self.assertEqual(1, len(ledgers)) ledger = ledgers[0] self.assertEqual(self.case.case_id, ledger.case_id) self.assertEqual(self.product_a._id, ledger.entry_id) self.assertEqual('stock', ledger.section_id) self.assertEqual(expected_balance, ledger.balance) def _assert_transactions(self, values, ignore_ordering=False): if should_use_sql_backend(DOMAIN): txs = LedgerAccessorSQL.get_ledger_transactions_for_case(self.case.case_id) self.assertEqual(len(values), len(txs)) if ignore_ordering: values = sorted(values, key=lambda v: (v.type, v.product_id)) txs = sorted(txs, key=lambda t: (t.type, t.entry_id)) for expected, tx in zip(values, txs): self.assertEqual(expected.type, tx.type) self.assertEqual(expected.product_id, tx.entry_id) self.assertEqual('stock', tx.section_id) self.assertEqual(expected.delta, tx.delta) self.assertEqual(expected.updated_balance, tx.updated_balance) def _expected_val(self, delta, updated_balance, type_=LedgerTransaction.TYPE_BALANCE, product_id=None): return TransactionValues(type_, product_id or self.product_a._id, delta, updated_balance)
class LedgerTests(TestCase): @classmethod def setUpClass(cls): FormProcessorTestUtils.delete_all_cases(DOMAIN) FormProcessorTestUtils.delete_all_xforms(DOMAIN) cls.product_a = make_product(DOMAIN, 'A Product', 'prodcode_a') cls.product_b = make_product(DOMAIN, 'B Product', 'prodcode_b') cls.product_c = make_product(DOMAIN, 'C Product', 'prodcode_c') def setUp(self): self.interface = FormProcessorInterface(domain=DOMAIN) self.factory = CaseFactory(domain=DOMAIN) self.case = self.factory.create_case() def _submit_ledgers(self, test_ledgers): return submit_case_blocks([ ledger.block.format( case_id=self.case.case_id, product_id=ledger.product_id or self.product_a._id, quantity=ledger.quantity ) for ledger in test_ledgers], DOMAIN ) def _set_balance(self, balance): self._submit_ledgers([TestLedger(BALANCE_BLOCK, balance, None)]) @run_with_all_backends def test_balance_submission(self): orignal_form_count = len(self.interface.get_case_forms(self.case.case_id)) self._submit_ledgers([TestLedger(BALANCE_BLOCK, 100, None)]) self._assert_ledger_state(100) # make sure the form is part of the case's history self.assertEqual(orignal_form_count + 1, len(self.interface.get_case_forms(self.case.case_id))) @run_with_all_backends def test_balance_submission_multiple(self): balances = { self.product_a._id: 100, self.product_b._id: 50, self.product_c._id: 25, } self._submit_ledgers([ TestLedger(BALANCE_BLOCK, balance, prod_id) for prod_id, balance in balances.items() ]) for prod_id, expected_balance in balances.items(): balance = self.interface.ledger_processor.get_current_ledger_value( UniqueLedgerReference( case_id=self.case.case_id, section_id='stock', entry_id=prod_id )) self.assertEqual(expected_balance, balance) @run_with_all_backends def test_balance_submission_with_prior_balance(self): self._set_balance(100) self._assert_ledger_state(100) self._set_balance(50) self._assert_ledger_state(50) self._set_balance(150) self._assert_ledger_state(150) @run_with_all_backends def test_transfer_submission(self): orignal_form_count = len(self.interface.get_case_forms(self.case.case_id)) self._submit_ledgers([TestLedger(TRANSFER_BLOCK, 100, None)]) self._assert_ledger_state(100) # make sure the form is part of the case's history self.assertEqual(orignal_form_count + 1, len(self.interface.get_case_forms(self.case.case_id))) @run_with_all_backends def test_transfer_submission_with_prior_balance(self): self._set_balance(100) self._submit_ledgers([TestLedger(TRANSFER_BLOCK, 100, None)]) self._assert_ledger_state(200) @run_with_all_backends def test_ledger_update_with_case_update(self): submit_case_blocks([ CaseBlock(case_id=self.case.case_id, update={'a': "1"}).as_string(), BALANCE_BLOCK.format( case_id=self.case.case_id, product_id=self.product_a._id, quantity=100 )], DOMAIN ) self._assert_ledger_state(100) case = CaseAccessors(DOMAIN).get_case(self.case.case_id) self.assertEqual("1", case.dynamic_case_properties()['a']) if settings.TESTS_SHOULD_USE_SQL_BACKEND: transactions = CaseAccessorSQL.get_transactions(self.case.case_id) self.assertEqual(3, len(transactions)) self.assertEqual(CaseTransaction.TYPE_FORM, transactions[0].type) # ordering not guaranteed since they have the same date self.assertEqual( {CaseTransaction.TYPE_FORM, CaseTransaction.TYPE_LEDGER}, {t.type for t in transactions[1:]} ) def _assert_ledger_state(self, expected_balance): ledgers = self.interface.ledger_processor.get_ledgers_for_case(self.case.case_id) self.assertEqual(1, len(ledgers)) ledger = ledgers[0] self.assertEqual(self.case.case_id, ledger.case_id) self.assertEqual(self.product_a._id, ledger.entry_id) self.assertEqual('stock', ledger.section_id) self.assertEqual(expected_balance, ledger.balance)