115 lines
6.1 KiB
Python
115 lines
6.1 KiB
Python
from datetime import datetime, time, timedelta, timezone as datetime_timezone
|
|
|
|
import pytest
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from apps.payments import policies, selectors, services
|
|
from apps.payments.forms import ReceiptForm
|
|
from apps.payments.models import (
|
|
PaymentAccessConfiguration, PaymentApprovalPolicy, PaymentNotice, PaymentReceipt,
|
|
PaymentReceiptRevision, ReceiptApprovalCycle,
|
|
)
|
|
from tests.test_payments import case, data, grant, submit, upload
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def test_daily_summary_uses_current_amounts_final_outcomes_and_all_pending(case, django_assert_num_queries):
|
|
independent_policy = PaymentApprovalPolicy.objects.create(name='بررسی فیش مستقل')
|
|
independent_policy.reviewers.add(case['first'], case['second'])
|
|
approved = submit(case)
|
|
services.decide_receipt(case['first'], approved.public_id, 1, 'APPROVE')
|
|
services.decide_receipt(case['second'], approved.public_id, 1, 'APPROVE')
|
|
rejected = submit(case, linked=False)
|
|
services.decide_receipt(case['first'], rejected.public_id, 1, 'REJECT', 'ناخوانا')
|
|
partial = submit(case)
|
|
services.decide_receipt(case['first'], partial.public_id, 1, 'APPROVE')
|
|
pending = submit(case, linked=False)
|
|
revised = submit(case)
|
|
services.decide_receipt(case['first'], revised.public_id, 1, 'REJECT', 'اصلاح مبلغ')
|
|
form = ReceiptForm(data(case, paid_amount='9000'), {'receipt_image': upload()}, customer=case['customer'])
|
|
assert form.is_valid(), form.errors
|
|
services.revise_receipt(case['customer'], revised.public_id, 1, form.cleaned_data)
|
|
PaymentReceiptRevision.objects.filter(version=1).update(amount=100)
|
|
yesterday = timezone.now() - timedelta(days=1)
|
|
PaymentReceipt.objects.filter(pk__in=[approved.pk, pending.pk]).update(created_at=yesterday)
|
|
# The written bank date is unrelated to when evidence arrived or was reviewed.
|
|
PaymentReceiptRevision.objects.update(paid_date=yesterday.date())
|
|
with django_assert_num_queries(2):
|
|
summary = selectors.daily_payment_summary(
|
|
PaymentReceipt.objects.all(), PaymentNotice.objects.filter(deleted_at__isnull=True))
|
|
assert summary == {
|
|
'date': timezone.localdate(), 'submitted_count': 3, 'submitted_amount': 9200,
|
|
'approved_count': 1, 'approved_amount': 100, 'rejected_count': 1, 'rejected_amount': 100,
|
|
'pending_count': 3, 'pending_amount': 9200, 'notice_count': 1,
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize('offset,included', [
|
|
(-1, False), (0, True), (24 * 60 * 60 - 1, True), (24 * 60 * 60, False),
|
|
])
|
|
def test_daily_summary_tehran_midnight_boundaries(case, monkeypatch, offset, included):
|
|
receipt = submit(case)
|
|
services.decide_receipt(case['senior'], receipt.public_id, 1, 'SENIOR_APPROVE', 'بررسی نهایی')
|
|
today = timezone.localdate()
|
|
start = timezone.make_aware(datetime.combine(today, time.min), timezone.get_default_timezone())
|
|
stamp = start + timedelta(seconds=offset)
|
|
PaymentReceipt.objects.filter(pk=receipt.pk).update(created_at=stamp)
|
|
PaymentNotice.objects.filter(pk=case['notice'].pk).update(created_at=stamp)
|
|
ReceiptApprovalCycle.objects.filter(revision__receipt=receipt).update(finalized_at=stamp)
|
|
monkeypatch.setattr(timezone, 'now', lambda: start + timedelta(hours=1))
|
|
# An active display timezone must not move the Tehran business-day boundary.
|
|
with timezone.override(datetime_timezone.utc):
|
|
summary = selectors.daily_payment_summary(PaymentReceipt.objects.all(), PaymentNotice.objects.all())
|
|
assert summary['date'] == today
|
|
assert summary['submitted_count'] == summary['approved_count'] == summary['notice_count'] == int(included)
|
|
assert summary['submitted_amount'] == summary['approved_amount']
|
|
assert bool(summary['submitted_amount']) == included
|
|
|
|
|
|
def test_daily_summary_excludes_old_finalizations_and_deleted_notices(case):
|
|
receipt = submit(case)
|
|
services.decide_receipt(case['first'], receipt.public_id, 1, 'REJECT', 'ناخوانا')
|
|
ReceiptApprovalCycle.objects.filter(revision__receipt=receipt).update(
|
|
finalized_at=timezone.now() - timedelta(days=1))
|
|
PaymentNotice.objects.filter(pk=case['notice'].pk).update(deleted_at=timezone.now())
|
|
summary = selectors.daily_payment_summary(
|
|
policies.visible(PaymentReceipt.objects.all(), case['creator']),
|
|
policies.visible(PaymentNotice.objects.all(), case['creator']))
|
|
assert summary['submitted_count'] == 1
|
|
assert summary['rejected_count'] == summary['rejected_amount'] == summary['notice_count'] == 0
|
|
|
|
|
|
def test_daily_summary_permission_scope_and_zero_state(case, client):
|
|
submit(case)
|
|
PaymentAccessConfiguration.objects.update_or_create(pk=1, defaults={'open_view': False})
|
|
client.force_login(case['outsider'])
|
|
response = client.get(reverse('staff_payments:desk'))
|
|
assert response.status_code == 200
|
|
summary = response.context['daily_summary']
|
|
assert all(value == 0 for key, value in summary.items() if key != 'date')
|
|
grant(case['outsider'], 'view_paymentreceipt')
|
|
response = client.get(reverse('staff_payments:desk'))
|
|
assert response.context['daily_summary']['submitted_count'] == 1
|
|
assert response.context['daily_summary']['notice_count'] == 0
|
|
grant(case['outsider'], 'view_paymentnotice')
|
|
assert client.get(reverse('staff_payments:desk')).context['daily_summary']['notice_count'] == 1
|
|
|
|
|
|
def test_daily_summary_stays_independent_of_tabs_search_and_pagination(case, client):
|
|
submit(case)
|
|
client.force_login(case['creator'])
|
|
endpoint = reverse('staff_payments:desk')
|
|
baseline = client.get(endpoint).context['daily_summary']
|
|
for parameters in [
|
|
{'tab': 'notices', 'notice_state': 'cancelled'},
|
|
{'tab': 'receipts', 'mine': '1', 'q': 'no-such-customer', 'page': '99'},
|
|
{'tab': 'receipts', 'status': 'APPROVED', 'kind': 'independent', 'from': '2000-01-01', 'to': '2000-01-02'},
|
|
]:
|
|
response = client.get(endpoint, parameters)
|
|
assert response.status_code == 200
|
|
assert response.context['daily_summary'] == baseline
|
|
assert response.context['page_obj'].paginator.count == 0
|
|
assert 'مبنای آمار' in response.content.decode()
|