fix fish variz name, dalilrad, sms, bale
This commit is contained in:
1 parent
90931a5a64
commit
35a1ec1794
30 files changed
+674
-39
No files matched your search
@@ -0,0 +1,167 @@
|
||||
"""Receipt identity, rejection outbox and destination warning regressions."""
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.customers.models import CustomerProfile
|
||||
from apps.integrations.sms.base import SMSResult
|
||||
from apps.notifications.management.commands import process_notifications as worker
|
||||
from apps.notifications.models import NotificationJob, SMSTemplate
|
||||
from apps.payments import services
|
||||
from apps.payments.deposit_identifier import generate_id_algorithm
|
||||
from apps.payments.forms import ReceiptForm
|
||||
from apps.payments.models import PaymentNotification, PaymentReceiptAlert, ReceiptDecision
|
||||
from tests.test_payment_links import open_notice
|
||||
from tests.test_payment_receipt_alerts import enabled, run
|
||||
from tests.test_payments import case, data, submit, upload
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
@pytest.mark.parametrize('via_link', [False, True])
|
||||
def test_payer_prefill_uses_profile_and_stays_editable(case, client, via_link):
|
||||
customer = case['customer']
|
||||
customer.first_name = 'نام حساب'
|
||||
customer.save()
|
||||
CustomerProfile.objects.update_or_create(user=customer, defaults={'first_name': 'علی', 'last_name': 'کریمی'})
|
||||
if via_link:
|
||||
open_notice(client, case)
|
||||
else:
|
||||
client.force_login(customer)
|
||||
page = client.get(reverse('payments:receipt_new'), {'notice': case['notice'].public_id})
|
||||
assert page.status_code == 200
|
||||
assert page.context['form']['depositor_name'].value() == 'علی کریمی'
|
||||
assert not page.context['form'].fields['depositor_name'].disabled
|
||||
|
||||
|
||||
@pytest.mark.parametrize('payer', ['', ' ', '\u200c\u200b', 'الف' * 151])
|
||||
def test_empty_or_invalid_payer_rejected_in_form_and_service(case, payer):
|
||||
form = ReceiptForm(data(case, depositor_name=payer), {'receipt_image': upload()}, customer=case['customer'])
|
||||
assert not form.is_valid() and 'depositor_name' in form.errors
|
||||
valid = ReceiptForm(data(case), {'receipt_image': upload()}, customer=case['customer'])
|
||||
assert valid.is_valid(), valid.errors
|
||||
with pytest.raises(ValidationError):
|
||||
services.submit_receipt(case['customer'], case['customer'], {**valid.cleaned_data, 'depositor_name': payer})
|
||||
|
||||
|
||||
def test_name_missing_from_post_is_not_silently_prefilled(case, client):
|
||||
customer = case['customer']
|
||||
customer.first_name = 'نام موجود'
|
||||
customer.save()
|
||||
client.force_login(customer)
|
||||
values = data(case)
|
||||
values.pop('depositor_name')
|
||||
response = client.post(reverse('payments:receipt_new'), {**values, 'receipt_image': upload()}, HTTP_ACCEPT='application/json')
|
||||
assert response.status_code == 422
|
||||
assert response.json()['errors']['depositor_name']
|
||||
|
||||
|
||||
def test_receipt_payer_shown_to_staff_and_nonce_cannot_change_identity(case, client):
|
||||
customer = case['customer']
|
||||
customer.first_name = 'نام مشتری'
|
||||
customer.save()
|
||||
values = data(case, depositor_name='شخص واریزکننده')
|
||||
form = ReceiptForm(values, {'receipt_image': upload()}, customer=customer)
|
||||
assert form.is_valid(), form.errors
|
||||
receipt = services.submit_receipt(customer, customer, form.cleaned_data)
|
||||
assert receipt.revisions.get().depositor_name == 'شخص واریزکننده'
|
||||
with pytest.raises(ValidationError):
|
||||
services.submit_receipt(customer, customer, {**form.cleaned_data, 'depositor_name': 'شخص دیگر'})
|
||||
client.force_login(case['first'])
|
||||
for endpoint in [reverse('staff_payments:desk'), reverse('staff_payments:receipt_detail', args=[receipt.public_id])]:
|
||||
content = client.get(endpoint).content.decode()
|
||||
assert 'نام مشتری' in content and 'شخص واریزکننده' in content
|
||||
|
||||
|
||||
def test_revision_keeps_payer_history_and_prefill(case, client):
|
||||
receipt = submit(case)
|
||||
services.decide_receipt(case['first'], receipt.public_id, 1, 'REJECT', 'اصلاح کنید')
|
||||
client.force_login(case['customer'])
|
||||
endpoint = reverse('payments:receipt_revise', args=[receipt.public_id])
|
||||
assert client.get(endpoint).context['form']['depositor_name'].value() == 'واریزکننده آزمایشی'
|
||||
response = client.post(endpoint, {**data(case, depositor_name='واریزکننده اصلاحشده'), 'version': 1, 'receipt_image': upload()})
|
||||
assert response.status_code == 302
|
||||
assert receipt.revisions.get(version=1).depositor_name == 'واریزکننده آزمایشی'
|
||||
assert receipt.revisions.get(version=2).depositor_name == 'واریزکننده اصلاحشده'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('payer,expected', [('علی کریمی', 'علی کریمی'), ('علي كريمي', 'علی کریمی'), ('رضا احمدی', 'علی کریمی (واریزکننده: رضا احمدی)')])
|
||||
def test_sms_and_bale_include_payer_without_duplicate_name(case, enabled, monkeypatch, payer, expected):
|
||||
customer = case['customer']
|
||||
customer.first_name, customer.last_name = 'علی', 'کریمی'
|
||||
customer.save()
|
||||
form = ReceiptForm(data(case, depositor_name=payer), {'receipt_image': upload()}, customer=customer)
|
||||
assert form.is_valid(), form.errors
|
||||
services.submit_receipt(customer, customer, form.cleaned_data)
|
||||
sent = []
|
||||
provider = SimpleNamespace(send_message=lambda mobile, body: sent.append(body) or SMSResult(True, 'sms-id'))
|
||||
monkeypatch.setattr(worker, 'get_sms_provider', lambda: provider)
|
||||
monkeypatch.setattr('apps.integrations.bale.providers.BaleBotProvider.send_photo',
|
||||
lambda self, chat, image, caption, **kwargs: sent.append(caption) or SMSResult(True, 'bale-id'))
|
||||
for alert in PaymentReceiptAlert.objects.select_related('job'):
|
||||
assert alert.context['customer_name'] == expected
|
||||
assert run(alert.job).status == 'SENT'
|
||||
assert len(sent) == 3
|
||||
assert all(expected in body and body.count('علی کریمی') == 1 for body in sent)
|
||||
|
||||
|
||||
def test_rejection_requires_confirmation_and_queues_exact_reason_once(case, client, monkeypatch):
|
||||
receipt = submit(case)
|
||||
client.force_login(case['first'])
|
||||
endpoint = reverse('staff_payments:decision', args=[receipt.public_id])
|
||||
reason = 'تصویر ناخوانا است؛ لطفاً تصویر واضح ارسال کنید.'
|
||||
network = Mock(side_effect=AssertionError('Must queue, not send during decision'))
|
||||
monkeypatch.setattr(worker, 'get_sms_provider', network)
|
||||
payload = {'version': 1, 'action': 'REJECT', 'reason': reason}
|
||||
client.post(endpoint, payload)
|
||||
receipt.refresh_from_db()
|
||||
assert receipt.status == 'PENDING' and not ReceiptDecision.objects.exists()
|
||||
for _ in range(2):
|
||||
assert client.post(endpoint, {**payload, 'confirm_rejection': '1'}).status_code == 302
|
||||
receipt.refresh_from_db()
|
||||
assert receipt.status == 'REJECTED'
|
||||
job = NotificationJob.objects.get(event_type='PAYMENT_RECEIPT_REJECTED')
|
||||
notification = PaymentNotification.objects.get(job=job)
|
||||
assert notification.context['rejection_reason'] == reason
|
||||
assert notification.cycle.revision.receipt == receipt
|
||||
network.assert_not_called()
|
||||
sent = []
|
||||
monkeypatch.setattr(worker, 'get_sms_provider', lambda: SimpleNamespace(
|
||||
send_message=lambda mobile, body: sent.append((mobile, body)) or SMSResult(True, 'rejection-sms')))
|
||||
assert run(job).status == 'SENT'
|
||||
assert sent[0][0] == case['customer'].mobile and reason in sent[0][1]
|
||||
assert '/p/' in sent[0][1]
|
||||
notification.refresh_from_db()
|
||||
assert notification.attempt_log.count() == 1
|
||||
|
||||
|
||||
def test_rejection_reason_survives_custom_template_and_queue_rollback(case, monkeypatch):
|
||||
receipt = submit(case)
|
||||
with pytest.raises(RuntimeError), transaction.atomic():
|
||||
services.decide_receipt(case['first'], receipt.public_id, 1, 'REJECT', 'ناخوانا')
|
||||
raise RuntimeError('rollback')
|
||||
assert not NotificationJob.objects.filter(event_type='PAYMENT_RECEIPT_REJECTED').exists()
|
||||
assert not ReceiptDecision.objects.exists()
|
||||
template = SMSTemplate.objects.get(event_type='PAYMENT_RECEIPT_REJECTED', unit__isnull=True)
|
||||
template.body = 'فیش {receipt_number} رد شد.'
|
||||
template.save()
|
||||
services.decide_receipt(case['first'], receipt.public_id, 1, 'REJECT', 'ناخوانا')
|
||||
job = NotificationJob.objects.get(event_type='PAYMENT_RECEIPT_REJECTED')
|
||||
messages = []
|
||||
monkeypatch.setattr(worker, 'get_sms_provider', lambda: SimpleNamespace(
|
||||
send_message=lambda mobile, body: messages.append(body) or SMSResult(True, 'id')))
|
||||
assert run(job).status == 'SENT' and 'ناخوانا' in messages[0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('linked', [False, True])
|
||||
def test_warning_uses_selected_bank_and_generated_customer_identifier(case, client, linked):
|
||||
client.force_login(case['customer'])
|
||||
params = {'notice': case['notice'].public_id} if linked else {}
|
||||
page = client.get(reverse('payments:receipt_new'), params)
|
||||
assert page.context['customer_deposit_id'] == generate_id_algorithm(case['customer'].local_mobile)
|
||||
assert page.context['account_requirements'][str(case['account'].pk)] == {'required': True, 'bank': case['account'].bank.name}
|
||||
assert 'ممکن است مبلغ واریزی به حساب شما برگشت بخورد' in page.content.decode()
|
||||
Reference in new issue
Block a user