Files
customerTicket/apps/payments/forms.py
T

128 lines
8.3 KiB
Python

import uuid
import jdatetime
from django import forms
from django.db.models import Q
from django.utils import timezone
from apps.accounts.models import User
from . import validators
from .models import BankInstitution, CompanyBankAccount, PaymentNotice
class BankAccountForm(forms.ModelForm):
revision_token = forms.CharField(required=False, widget=forms.HiddenInput)
class Meta:
model = CompanyBankAccount
fields = ['revision_token', 'name', 'bank', 'holder', 'account_number', 'iban', 'card_number', 'requires_deposit_id', 'is_active', 'available_to_staff', 'available_to_customers', 'sort_order']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.initial['revision_token'] = self.instance.updated_at.isoformat()
def clean_account_number(self):
return validators.bank_number(self.cleaned_data['account_number'])
def clean_card_number(self):
value = validators.bank_number(self.cleaned_data['card_number'])
if value and (len(value) != 16 or sum((int(n) * (2 if i % 2 == 0 else 1)) // 10 + (int(n) * (2 if i % 2 == 0 else 1)) % 10 for i, n in enumerate(value)) % 10):
raise forms.ValidationError('شماره کارت ۱۶ رقمی معتبر وارد کنید.')
return value
def clean_iban(self):
return validators.iban(self.cleaned_data['iban'])
def clean(self):
data = super().clean()
if self.instance.pk and data.get('revision_token') != self.instance.updated_at.isoformat():
raise forms.ValidationError('حساب از زمان باز کردن فرم تغییر کرده است؛ صفحه را تازه کنید و تغییرات را دوباره بررسی کنید.')
if not any(data.get(key) for key in ('account_number', 'iban', 'card_number')):
raise forms.ValidationError('حداقل شماره حساب، شبا یا کارت را وارد کنید.')
return data
class NoticeForm(forms.Form):
submission_key = forms.UUIDField(widget=forms.HiddenInput)
customer = forms.ModelChoiceField(label='مشتری', queryset=User.objects.none(), widget=forms.HiddenInput,
error_messages={'required': 'مشتری را از نتایج جست‌وجو انتخاب کنید.',
'invalid_choice': 'مشتری انتخاب‌شده معتبر یا فعال نیست؛ دوباره جست‌وجو کنید.'})
account = forms.ModelChoiceField(label='حساب مقصد', queryset=CompanyBankAccount.objects.none())
subject = forms.CharField(label='موضوع', max_length=200)
amount = forms.CharField(label='مبلغ (ریال)')
deposit_id = forms.CharField(label='شناسه واریز', max_length=100, required=False)
invoice_number = forms.CharField(label='شماره فاکتور', max_length=100, required=False)
description = forms.CharField(label='توضیحات', required=False, widget=forms.Textarea)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initial.setdefault('submission_key', uuid.uuid4())
self.fields['customer'].queryset = User.objects.filter(account_type='CUSTOMER', is_active=True).select_related('customer_profile')
self.fields['customer'].label_from_instance = lambda u: f'{getattr(u, "customer_profile", u)} — {u.local_mobile}'
self.fields['account'].queryset = CompanyBankAccount.objects.filter(is_active=True, available_to_staff=True).select_related('bank')
def clean_amount(self):
return validators.amount(self.cleaned_data['amount'])
def clean(self):
data = super().clean()
if data.get('account') and data['account'].requires_deposit_id and not data.get('deposit_id', '').strip():
self.add_error('deposit_id', 'این حساب شناسه‌دار است؛ شناسه واریز مشتری را حتماً وارد کنید.')
return data
class ReceiptForm(forms.Form):
receipt_mode = forms.ChoiceField(label='نوع واریز', choices=[('linked', 'واریز مرتبط (پیشنهادی)'), ('independent', 'واریز مستقل')], widget=forms.RadioSelect, initial='linked')
notice_id = forms.ModelChoiceField(label='اعلامیه واریز', queryset=PaymentNotice.objects.none(), required=False)
paid_amount = forms.CharField(label='مبلغ پرداخت‌شده (ریال)')
destination_account = forms.ModelChoiceField(label='به کدام حساب ما واریز کردید؟', queryset=CompanyBankAccount.objects.none())
deposit_id = forms.CharField(label='شناسه واریز که با آن پرداخت انجام داده‌اید (اختیاری)', max_length=100, required=False)
origin_bank = forms.ModelChoiceField(label='بانک مبدأ (اختیاری)', queryset=BankInstitution.objects.filter(is_active=True), required=False)
origin_other = forms.CharField(label='نام بانک دیگر (اختیاری)', max_length=100, required=False)
paid_day = forms.ChoiceField(label='روز', choices=[('', 'روز')] + [(str(i), str(i)) for i in range(1, 32)])
paid_month = forms.ChoiceField(label='ماه', choices=[('', 'ماه')] + list(zip(map(str, range(1, 13)), ['فروردین','اردیبهشت','خرداد','تیر','مرداد','شهریور','مهر','آبان','آذر','دی','بهمن','اسفند'])))
paid_year = forms.ChoiceField(label='سال')
receipt_image = forms.FileField(label='تصویر فیش', widget=forms.ClearableFileInput(attrs={'accept': 'image/*'}))
tracking_number = forms.CharField(label='شماره پیگیری (اختیاری)', max_length=100, required=False)
description = forms.CharField(label='توضیحات (اختیاری)', required=False, widget=forms.Textarea)
submission_key = forms.UUIDField(widget=forms.HiddenInput)
def __init__(self, *args, customer, notice_ids=None, **kwargs):
super().__init__(*args, **kwargs)
notices = PaymentNotice.objects.filter(customer=customer, cancelled_at__isnull=True)
if notice_ids is not None:
notices = notices.filter(pk__in=notice_ids)
self.fields['notice_id'].queryset = notices
self.fields['notice_id'].label_from_instance = lambda n: f'{n.subject} — {n.amount:,} ریال — {n.invoice_number}'
accounts = Q(is_active=True, available_to_customers=True) | Q(is_active=True, paymentnotice__in=notices)
self.fields['destination_account'].queryset = CompanyBankAccount.objects.filter(accounts).distinct()
current = jdatetime.date.fromgregorian(date=timezone.localdate())
self.fields['paid_year'].choices = [('', 'سال')] + [(str(y), str(y)) for y in range(current.year, current.year - 11, -1)]
self.initial.setdefault('submission_key', uuid.uuid4())
self.initial.setdefault('paid_month', str(current.month))
self.initial.setdefault('paid_year', str(current.year))
def clean_paid_amount(self):
return validators.amount(self.cleaned_data['paid_amount'])
def clean_receipt_image(self):
upload = self.cleaned_data['receipt_image']
self.image_metadata = validators.image(upload)
return upload
def clean(self):
data = super().clean()
notice = data.get('notice_id') if data.get('receipt_mode') == 'linked' else None
data['notice_id'] = notice
if data.get('receipt_mode') == 'linked' and not notice:
self.add_error('notice_id', 'اعلامیه را انتخاب کنید یا واریز مستقل را بزنید.')
try:
data['paid_date'] = validators.paid_date(data.get('paid_day'), data.get('paid_month'), data.get('paid_year'))
if self.data.get('paid_date') and self.data['paid_date'] != data['paid_date'].isoformat():
raise forms.ValidationError('تاریخ ارسالی با تاریخ انتخاب‌شده سازگار نیست؛ دوباره انتخاب کنید.')
except forms.ValidationError as error:
self.add_error('paid_day', error)
account = data.get('destination_account')
if account and not account.available_to_customers and (not notice or notice.account_id != account.pk):
self.add_error('destination_account', 'این حساب فقط برای اعلامیه مربوط به آن قابل انتخاب است.')
return data