def test_invoice_generate_negative_balance(member, invoice_directory): """Tests that generated invoices that resulted in a negative balance (debt) are carried over the next month. """ from pootle_statistics.models import Submission WORDCOUNT = 5 TRANSLATION_RATE = 5 WORK_DONE = WORDCOUNT * TRANSLATION_RATE CORRECTION = -100 SUBTOTAL = WORK_DONE + CORRECTION month = timezone.datetime(2014, 04, 01) invoice = Invoice(member, FAKE_CONFIG, month=month, add_correction=True) # Set some rates member.rate = TRANSLATION_RATE member.save() # Work done + negative correction leaves amounts in negative scorelog_kwargs = { 'wordcount': WORDCOUNT, 'similarity': 0, 'action_code': TranslationActionCodes.NEW, 'creation_time': month, 'user': member, 'submission': Submission.objects.first(), } ScoreLogFactory(**scorelog_kwargs) paid_task_kwargs = { 'amount': CORRECTION, 'rate': 1, 'datetime': month, 'user': member, 'task_type': PaidTaskTypes.CORRECTION, } PaidTaskFactory(**paid_task_kwargs) # Inspect numbers prior to actual generation amounts = invoice._calculate_amounts() assert amounts['subtotal'] == SUBTOTAL assert amounts['correction'] == CORRECTION assert amounts['total'] == SUBTOTAL assert not invoice.is_carried_over invoice.generate() _check_single_paidtask(invoice, SUBTOTAL) assert PaidTask.objects.filter( task_type=PaidTaskTypes.CORRECTION).count() == 3 # Now numbers have been adjusted assert invoice.amounts['balance'] == SUBTOTAL assert invoice.amounts['correction'] == SUBTOTAL * -1 # carry-over assert invoice.amounts['total'] == 0 assert not invoice.needs_carry_over(invoice.amounts['subtotal']) assert invoice.is_carried_over
def test_invoice_get_amounts(member): """Tests accessing amounts when they haven't been calculated yet. """ user = UserFactory.build() invoice = Invoice(user, FAKE_CONFIG) with pytest.raises(AssertionError): invoice.amounts invoice.generate() assert invoice.amounts is not None
def test_invoice_generate_balance_with_carry_over(member, invoice_directory): """Tests that balance is properly reported even if a carry-over already existed. """ from pootle_statistics.models import Submission WORDCOUNT = 5 TRANSLATION_RATE = 5 WORK_DONE = WORDCOUNT * TRANSLATION_RATE CORRECTION = -100 SUBTOTAL = WORK_DONE + CORRECTION month = timezone.datetime(2014, 04, 01) # Set some rates member.rate = TRANSLATION_RATE member.save() # Record work scorelog_kwargs = { 'wordcount': WORDCOUNT, 'similarity': 0, 'action_code': TranslationActionCodes.NEW, 'creation_time': month, 'user': member, 'submission': Submission.objects.first(), } ScoreLogFactory(**scorelog_kwargs) paid_task_kwargs = { 'amount': CORRECTION, 'rate': 1, 'datetime': month, 'user': member, 'task_type': PaidTaskTypes.CORRECTION, } PaidTaskFactory(**paid_task_kwargs) invoice = Invoice(member, FAKE_CONFIG, month=month, add_correction=True) assert not invoice.is_carried_over invoice.generate() assert invoice.is_carried_over assert invoice.amounts['balance'] == SUBTOTAL invoice_copy = Invoice(member, FAKE_CONFIG, month=month, add_correction=True) assert invoice_copy.is_carried_over invoice_copy.generate() assert invoice_copy.is_carried_over assert invoice_copy.amounts['balance'] == SUBTOTAL
def test_invoice_generate_balance_with_carry_over(member, invoice_directory): """Tests that balance is properly reported even if a carry-over already existed. """ from pootle_statistics.models import Submission WORDCOUNT = 5 TRANSLATION_RATE = 5 WORK_DONE = WORDCOUNT * TRANSLATION_RATE CORRECTION = -100 SUBTOTAL = WORK_DONE + CORRECTION month = timezone.make_aware(timezone.datetime(2014, 4, 1)) # Set some rates member.rate = TRANSLATION_RATE member.save() # Record work scorelog_kwargs = { "wordcount": WORDCOUNT, "similarity": 0, "action_code": TranslationActionCodes.NEW, "creation_time": month, "user": member, "submission": Submission.objects.first(), } ScoreLogFactory(**scorelog_kwargs) paid_task_kwargs = { "amount": CORRECTION, "rate": 1, "datetime": month, "user": member, "task_type": PaidTaskTypes.CORRECTION, } PaidTaskFactory(**paid_task_kwargs) invoice = Invoice(member, FAKE_CONFIG, month=month, add_correction=True) assert not invoice.is_carried_over invoice.generate() assert invoice.is_carried_over assert invoice.amounts["balance"] == SUBTOTAL invoice_copy = Invoice(member, FAKE_CONFIG, month=month, add_correction=True) assert invoice_copy.is_carried_over invoice_copy.generate() assert invoice_copy.is_carried_over assert invoice_copy.amounts["balance"] == SUBTOTAL
def test_invoice_generate_add_correction(member, invoice_directory): """Tests that generating invoices multiple times for the same month + user will add corrections only once. """ from pootle_statistics.models import Submission EVENT_COUNT = 5 WORDCOUNT = 5 TRANSLATION_RATE = 0.5 INITIAL_SUBTOTAL = EVENT_COUNT * WORDCOUNT * TRANSLATION_RATE MINIMAL_PAYMENT = 20 month = get_previous_month() config = dict({ 'minimal_payment': MINIMAL_PAYMENT, }, **FAKE_CONFIG) invoice = Invoice(member, config, month=month, add_correction=True) # Set some rates member.rate = TRANSLATION_RATE member.save() # Fake some activity that will leave amounts below the minimum bar: # EVENT_COUNT * WORDCOUNT * TRANSLATION_RATE < MINIMAL_PAYMENT for i in range(EVENT_COUNT): scorelog_kwargs = { 'wordcount': WORDCOUNT, 'similarity': 0, 'action_code': TranslationActionCodes.NEW, 'creation_time': month, 'user': member, 'submission': Submission.objects.all()[i], } ScoreLogFactory(**scorelog_kwargs) # Generate an invoice first amounts = invoice.get_total_amounts() assert amounts['subtotal'] == INITIAL_SUBTOTAL assert invoice.should_add_correction(amounts['subtotal']) invoice.generate() _check_single_paidtask(invoice, INITIAL_SUBTOTAL) # Subsequent invoice generations must not add any corrections for i in range(5): invoice.get_total_amounts.cache_clear() # clears the LRU cache amounts = invoice.get_total_amounts() assert amounts['subtotal'] == 0 assert not invoice.should_add_correction(amounts['subtotal']) invoice.generate() _check_single_paidtask(invoice, INITIAL_SUBTOTAL)
def test_invoice_amounts_below_minimal_payment(member, monkeypatch): """Tests total amounts' correctness when the accrued total is below the minimal payment bar. """ config = dict({"minimal_payment": 10, "extra_add": 5}, **FAKE_CONFIG) invoice = Invoice(member, config, add_correction=True) rates = (0.5, 0.5, 0.5) monkeypatch.setattr(invoice, "get_rates", lambda: rates) amounts = (5, 5, 5, 0) monkeypatch.setattr(invoice, "_get_full_user_amounts", lambda x: amounts) invoice.generate() assert invoice.amounts["subtotal"] == 3 * (amounts[0] * rates[0]) assert invoice.amounts["balance"] == 3 * (amounts[0] * rates[0]) assert invoice.amounts["total"] == 0 assert invoice.amounts["extra_amount"] == 0
def test_invoice_amounts_with_extra_add(member, monkeypatch): """Tests total amounts' correctness when there is an extra amount to be added to the accrued total. """ extra_add = 5 user = UserFactory.build() config = dict({"extra_add": extra_add}, **FAKE_CONFIG) invoice = Invoice(user, config, add_correction=True) rates = (0.5, 0.5, 0.5) monkeypatch.setattr(invoice, "get_rates", lambda: rates) amounts = (5, 5, 5, 0) monkeypatch.setattr(invoice, "_get_full_user_amounts", lambda x: amounts) invoice.generate() assert invoice.amounts["subtotal"] == 3 * (amounts[0] * rates[0]) assert invoice.amounts["balance"] is None assert invoice.amounts["total"] == 3 * (amounts[0] * rates[0]) + extra_add assert invoice.amounts["extra_amount"] == extra_add
def test_invoice_amounts_below_minimal_payment(member, monkeypatch): """Tests total amounts' correctness when the accrued total is below the minimal payment bar. """ config = dict({ 'minimal_payment': 10, 'extra_add': 5, }, **FAKE_CONFIG) invoice = Invoice(member, config, add_correction=True) rates = (0.5, 0.5, 0.5) monkeypatch.setattr(invoice, 'get_rates', lambda: rates) amounts = (5, 5, 5, 0) monkeypatch.setattr(invoice, '_get_full_user_amounts', lambda x: amounts) invoice.generate() assert invoice.amounts['subtotal'] == 3 * (amounts[0] * rates[0]) assert invoice.amounts['balance'] == 3 * (amounts[0] * rates[0]) assert invoice.amounts['total'] == 0 assert invoice.amounts['extra_amount'] == 0
def test_invoice_generate_add_carry_over(member, invoice_directory): """Tests that generating invoices multiple times for the same month + user will add carry-over corrections only once. """ from pootle_statistics.models import Submission EVENT_COUNT = 5 WORDCOUNT = 5 TRANSLATION_RATE = 0.5 INITIAL_SUBTOTAL = EVENT_COUNT * WORDCOUNT * TRANSLATION_RATE MINIMAL_PAYMENT = 20 month = timezone.datetime(2014, 04, 01) config = dict({ 'minimal_payment': MINIMAL_PAYMENT, }, **FAKE_CONFIG) invoice = Invoice(member, config, month=month, add_correction=True) # Set some rates member.rate = TRANSLATION_RATE member.save() # Fake some activity that will leave amounts below the minimum bar: # EVENT_COUNT * WORDCOUNT * TRANSLATION_RATE < MINIMAL_PAYMENT for i in range(EVENT_COUNT): scorelog_kwargs = { 'wordcount': WORDCOUNT, 'similarity': 0, 'action_code': TranslationActionCodes.NEW, 'creation_time': month, 'user': member, 'submission': Submission.objects.all()[i], } ScoreLogFactory(**scorelog_kwargs) # Inspect numbers prior to actual generation amounts = invoice._calculate_amounts() assert amounts['subtotal'] == INITIAL_SUBTOTAL assert amounts['correction'] == 0 assert amounts['total'] == INITIAL_SUBTOTAL assert not invoice.is_carried_over # Generate an invoice first invoice.generate() _check_single_paidtask(invoice, INITIAL_SUBTOTAL) assert PaidTask.objects.filter( task_type=PaidTaskTypes.CORRECTION).count() == 2 # Now numbers have been adjusted assert invoice.amounts['balance'] == INITIAL_SUBTOTAL assert invoice.amounts['correction'] == INITIAL_SUBTOTAL * -1 # carry-over assert invoice.amounts['total'] == 0 assert not invoice.needs_carry_over(invoice.amounts['subtotal']) assert invoice.is_carried_over # Inspecting numbers doesn't alter anything amounts = invoice._calculate_amounts() assert amounts['subtotal'] == 0 assert amounts['correction'] == INITIAL_SUBTOTAL * -1 assert amounts['total'] == 0 # Subsequent invoice generations must not add any corrections invoice.generate() _check_single_paidtask(invoice, INITIAL_SUBTOTAL) assert PaidTask.objects.filter( task_type=PaidTaskTypes.CORRECTION).count() == 2 assert invoice.amounts['subtotal'] == 0 assert invoice.amounts['correction'] == INITIAL_SUBTOTAL * -1 assert invoice.amounts['total'] == 0 assert not invoice.needs_carry_over(invoice.amounts['subtotal']) assert invoice.is_carried_over