From b410078649ab14da738f8c39727eb1a2662a76d1 Mon Sep 17 00:00:00 2001 From: Sayyid Hamid Mahdavi Date: Sat, 9 Aug 2025 12:29:50 +0330 Subject: [PATCH] new templates --- apps/pg/models.py | 14 +- apps/pg/views.py | 8 +- main/settings.py | 6 +- static/css/datetimepicker.css | 34 +++ static/js/datetimepicker.js | 396 ++++++++++++++++++++++++++++++ templates/admin/input_filter.html | 15 ++ templates/pg/payment_failure.html | 179 ++++++++++++++ templates/pg/payment_success.html | 175 +++++++++++++ 8 files changed, 821 insertions(+), 6 deletions(-) create mode 100644 static/css/datetimepicker.css create mode 100644 static/js/datetimepicker.js create mode 100644 templates/admin/input_filter.html create mode 100644 templates/pg/payment_failure.html create mode 100644 templates/pg/payment_success.html diff --git a/apps/pg/models.py b/apps/pg/models.py index 8530291..b7f9615 100644 --- a/apps/pg/models.py +++ b/apps/pg/models.py @@ -91,6 +91,15 @@ class PaymentRequest(models.Model): self.save() + @property + def reference_number(self): + pna = PNA.objects.filter(payment_request=self).first() + if pna: + return pna.pk + + else: + return '00000000' + # TODO: complete and use this def change_state(self, from_state, to_state, error_message=None, **data): if self.state == from_state: @@ -222,7 +231,7 @@ class PNA(BasePGModel): def confirm_request(self, request=None): if self.payment_request.state not in [StateChoices.IPG_PENDING]: messages.error(request, "وضعیت تراکنش در حالت مناسبی نیست.") - return + return False, "وضعیت تراکنش در حالت مناسبی نیست." print(dict(request.POST)) post_dict = dict(request.POST) @@ -246,13 +255,16 @@ class PNA(BasePGModel): self.payment_request.charge_wallet_balance() messages.success(request, 'شارژ انجام شد') + return True, 'شارژ انجام شد' except Exception as e: print(e) messages.error(request, str(e)) + return False, "خطایی رخ داده است." else: print(request.POST) messages.error(request, "نتیجه تراکنش از سمت درگاه دریافت نشد.") + return False, "نتیجه تراکنش از سمت درگاه دریافت نشد." def reverse_request(self): raise NotImplementedError() diff --git a/apps/pg/views.py b/apps/pg/views.py index 4816098..79d51e5 100644 --- a/apps/pg/views.py +++ b/apps/pg/views.py @@ -83,7 +83,8 @@ class PaymentRequestView(UpdateView): @method_decorator(csrf_exempt, name='dispatch') class PaymentConfirmView(UpdateView): - template_name = 'pg/payment-confirm.html' + template_name = 'pg/payment_failure.html' + # template_name = 'pg/payment_success.html' form_class = PaymentRequestForm # pk_url_kwarg = 'uuid' slug_field = 'uuid' @@ -118,8 +119,11 @@ class PaymentConfirmView(UpdateView): return response tool = self.object.get_gateway_tools() - tool.confirm_request(request) + state, message = tool.confirm_request(request) + if state: + self.template_name = 'pg/payment_success.html' context = self.get_context_data(object=self.object) + context['message'] = message return self.render_to_response(context) diff --git a/main/settings.py b/main/settings.py index d49bd35..edd86e3 100644 --- a/main/settings.py +++ b/main/settings.py @@ -244,6 +244,6 @@ CACHES = { } } -PNA_CORPORATION_PIN = config('PNA_CORPORATION_PIN') -WALLET_BASE_PUBLIC_URL = config('WALLET_BASE_PUBLIC_URL') -IPG_BASE_PUBLIC_URL = config('IPG_BASE_PUBLIC_URL') +PNA_CORPORATION_PIN = config('PNA_CORPORATION_PIN') +WALLET_BASE_PUBLIC_URL = config('WALLET_BASE_PUBLIC_URL') +IPG_BASE_PUBLIC_URL = config('IPG_BASE_PUBLIC_URL') diff --git a/static/css/datetimepicker.css b/static/css/datetimepicker.css new file mode 100644 index 0000000..ea9cf87 --- /dev/null +++ b/static/css/datetimepicker.css @@ -0,0 +1,34 @@ +.cursorily{ cursor: pointer;} +.hov:hover{ color: #000;} +.ico-size{font-size: 16px;} +.ico-size-month{font-size: 26px!important; line-height: 26px!important;} +.ico-size-large{ font-size: 40px!important; line-height: 30px;} +.dtp_main{ border: solid 1px #eee; border-radius: 3px; background-color: #fff; padding: 8px 0 8px 8px;} +.dtp_main span, .dtp_main i{ display: inline-block; padding-right: 8px;} +.dtp_modal-win{position: fixed;left: 0; top: 0; width: 100%; height: 100%; + z-index: 999; background-color: #eeeeee; opacity: 0.6;} +.dtp_modal-content{ background-color: #fff; border-radius: 10px; width: 624px; + position: absolute; z-index: 1000; top: 100px; left: 100px; font-size: 16px;font-weight: normal;} +.dtp_modal-content-no-time{ background-color: #fff; border-radius: 10px; width: 312px; + position: absolute; z-index: 1000; top: 100px; left: 100px; font-size: 16px;font-weight: normal;} +.dtp_modal-title{ border-bottom: solid 3px #54646b; padding: 16px 36px; margin-bottom: 16px; font-size: 22px; } +.dtp_modal-cell-date{ width: 312px; float: right; margin-bottom: 22px; margin-top: 6px;} +.dtp_modal-cell-time{width: 311px; float: left; direction: ltr; border-right: solid 1px #000;} +.dtp_modal-months{ color: #7d7d7d; text-align: center; font-size: 20px; padding: 0 20px;} +.dtp_modal-months span{ display: inline-block; padding: 10px 20px; width: 182px;} +.dtp_modal-calendar{ width: 266px; margin-left: 22px; } +.dtp_modal-calendar-cell{ width: 38px; padding: 7px 0; display: inline-block; text-align: center;} +.dtp_modal-colored{ color: #54646b; } +.dtp_modal-grey{ color: #7d7d7d; } +.dtp_modal-cell-selected{ background-color: #54646b; border-radius: 48%; transition: background-color 1s ease-out;} +.dtp_modal-time-block{ height: 212px; width: 310px; } +.dpt_modal-button{ background-color: #54646b; color: #fff; font-size: 24px; padding: 8px 40px; float: left; + text-align: center; display: inline-block; margin-left: 22px; cursor: pointer; border: solid 1px #fff; +border-radius: 3px; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);} +.dtp_modal-time-line{ text-align: center; color: #7d7d7d; font-size: 20px; padding-top: 15px; } +.dtp_modal-time-mechanic{ padding-top: 16px;} +.dtp_modal-append{ color: #7d7d7d; padding-left: 108px; font-weight: normal; } +.dtp_modal-midle{ display: inline-block; width: 40px; } +.dtp_modal-midle-dig{display: inline-block; width: 16px; text-align: center; } +.dtp_modal-digits{ font-size: 50px; padding-left: 96px;} +.dtp_modal-digit{ } diff --git a/static/js/datetimepicker.js b/static/js/datetimepicker.js new file mode 100644 index 0000000..adb871e --- /dev/null +++ b/static/js/datetimepicker.js @@ -0,0 +1,396 @@ +(function ($) { + 'use strict'; + $.fn.dateTimePicker = function (options) { + + var settings = $.extend({ + selectData: "now", + dateFormat: "YYYY-MM-DD HH:mm", + showTime: true, + locale: 'en', + positionShift: { top: 20, left: 0}, + title: "Select Date and Time", + buttonTitle: "Select", + allowBackwards: false + }, options); + moment.locale(settings.locale); + var elem = this; + var limitation = {"hour": 23, "minute": 59}; + var mousedown = false; + var timeout = 800; + var selectDate = settings.selectData == "now" ? moment() : moment(settings.selectData, settings.dateFormat); + if (selectDate < moment() && (settings.allowBackwards == false)) { + selectDate = moment(); + } + var startDate = copyDate(moment()); + var lastSelected = copyDate(selectDate); + return this.each(function () { + if (lastSelected != selectDate) { + selectDate = copyDate(lastSelected); + } + elem.addClass("dtp_main"); + updateMainElemGlobal(); + // elem.text(selectDate.format(settings.dateFormat)); + function updateMainElemGlobal() { + var arrF = settings.dateFormat.split(' '); + if (settings.showTime && arrF.length != 2) { + arrF.length = 2; + arrF[0] = 'DD/MM/YY'; + arrF[1] = 'HH:mm'; + } + var $s = $(''); + $s.text(lastSelected.format(arrF[0])); + elem.empty(); + elem.append($s); + $s = $(''); + $s.addClass('fa fa-calendar ico-size'); + elem.append($s); + if (settings.showTime) { + $s = $(''); + $s.text(lastSelected.format(arrF[1])); + elem.append($s); + $s = $(''); + $s.addClass('fa fa-clock-o ico-size'); + elem.append($s); + } + } + elem.on('click', function () { + var $win = $('
'); + $win.addClass("dtp_modal-win"); + var $body = $('body'); + $body.append($win); + var $content = createContent(); + $body.append($content); + var offset = elem.offset(); + $content.css({top: (offset.top + settings.positionShift.top) + "px", left: (offset.left + settings.positionShift.left) + "px"}); + feelDates(selectDate); + $win.on('click', function () { + $content.remove(); + $win.remove(); + }) + if (settings.showTime) { + attachChangeTime(); + var $fieldTime = $('#field-time'); + var $hour = $fieldTime.find('#d-hh'); + var $minute = $fieldTime.find('#d-mm'); + } + + function feelDates(selectM) { + var $fDate = $content.find('#field-data'); + $fDate.empty(); + $fDate.append(createMonthPanel(selectM)); + $fDate.append(createCalendar(selectM)); + } + + function createCalendar(selectedMonth) { + var $c = $('
'); + $c.addClass('dtp_modal-calendar'); + for (var i = 0; i < 7; i++) { + var $e = $('
'); + $e.addClass('dtp_modal-calendar-cell dtp_modal-colored'); + $e.text(moment().weekday(i).format('ddd')); + $c.append($e); + } + var m = copyDate(selectedMonth); + m.date(1); + // console.log(m.format('DD--MM--YYYY')); + // console.log(selectData.format('DD--MM--YYYY')); + // console.log(m.weekday()); + var flagStart = totalMonths(selectedMonth) === totalMonths(startDate); + var flagSelect = totalMonths(lastSelected) === totalMonths(selectedMonth); + var cerDay = parseInt(selectedMonth.format('D')); + var dayNow = parseInt(startDate.format('D')); + for (var i = 0; i < 6; i++) { + for (var j = 0; j < 7; j++) { + var $b = $('
'); + $b.html(' '); + $b.addClass('dtp_modal-calendar-cell'); + if (m.month() == selectedMonth.month() && m.weekday() == j) { + var day = parseInt(m.format('D')); + $b.text(day); + if (flagStart && day < dayNow) { + + if (settings.allowBackwards == false) { + $b.addClass('dtp_modal-grey'); + } else { + $b.addClass('cursorily'); + $b.bind('click', changeDate); + } + + } + else if (flagSelect && day == cerDay) { + $b.addClass('dtp_modal-cell-selected'); + } + else { + $b.addClass('cursorily'); + $b.bind('click', changeDate); + } + m.add(1, 'days'); + } + $c.append($b); + } + } + return $c; + } + + function changeDate() { + + var $div = $(this); + selectDate.date($div.text()); + lastSelected = copyDate(selectDate); + updateDate(); + var $fDate = $content.find('#field-data'); + var old = $fDate.find('.dtp_modal-cell-selected'); + old.removeClass('dtp_modal-cell-selected'); + old.addClass('cursorily'); + $div.addClass('dtp_modal-cell-selected'); + $div.removeClass('cursorily'); + old.bind('click', changeDate); + $div.unbind('click'); + // console.log(selectDate.format('DD-MM-YYYY')); + } + + function createMonthPanel(selectMonth) { + var $d = $('
'); + $d.addClass('dtp_modal-months'); + var $s = $(''); + $s.addClass('fa fa-angle-left cursorily ico-size-month hov'); + //$s.attr('data-fa-mask', 'fas fa-circle'); + $s.bind('click', prevMonth); + $d.append($s); + $s = $(''); + $s.text(selectMonth.format("MMMM YYYY")); + $d.append($s); + $s = $(''); + $s.addClass('fa fa-angle-right cursorily ico-size-month hov'); + $s.bind('click', nextMonth); + $d.append($s); + return $d; + } + + function close() { + if (settings.showTime) { + lastSelected.hour(parseInt($hour.text())); + lastSelected.minute(parseInt($minute.text())); + selectDate.hour(parseInt($hour.text())); + selectDate.minute(parseInt($minute.text())); + } + updateDate(); + $content.remove(); + $win.remove(); + } + + function nextMonth() { + selectDate.add(1, 'month'); + feelDates(selectDate); + } + + function prevMonth() { + if (totalMonths(selectDate) > totalMonths(startDate) || settings.allowBackwards == true) { + selectDate.add(-1, 'month'); + feelDates(selectDate); + } + } + + function attachChangeTime() { + var $angles = $($content).find('i[id^="angle-"]'); + // $angles.bind('click', changeTime); + $angles.bind('mouseup', function () { + mousedown = false; + timeout = 800; + }); + $angles.bind('mousedown', function () { + mousedown = true; + changeTime(this); + }); + } + + function changeTime(el) { + var $el = this || el; + $el = $($el); + ///angle-up-hour angle-up-minute angle-down-hour angle-down-minute + var arr = $el.attr('id').split('-'); + var increment = 1; + if (arr[1] == 'down') { + increment = -1; + } + appendIncrement(arr[2], increment); + setTimeout(function () { + autoIncrement($el); + }, timeout); + } + + function autoIncrement(el) { + if (mousedown) { + if (timeout > 200) { + timeout -= 200; + } + changeTime(el); + } + } + + function appendIncrement(typeDigits, increment) { + + var $i = typeDigits == "hour" ? $hour : $minute; + var val = parseInt($i.text()) + increment; + if (val < 0) { + val = limitation[typeDigits]; + } + else if (val > limitation[typeDigits]) { + val = 0; + } + $i.text(formatDigits(val)); + } + + function formatDigits(val) { + + if (val < 10) { + return '0' + val; + } + return val; + } + + function createTimer() { + var $div = $('
'); + $div.addClass('dtp_modal-time-mechanic'); + var $panel = $('
'); + $panel.addClass('dtp_modal-append'); + var $i = $(''); + $i.attr('id', 'angle-up-hour'); + $i.addClass('fa fa-angle-up ico-size-large cursorily hov'); + $panel.append($i); + var $m = $(''); + $m.addClass('dtp_modal-midle'); + $panel.append($m); + $i = $(''); + $i.attr('id', 'angle-up-minute'); + $i.addClass('fa fa-angle-up ico-size-large cursorily hov'); + $panel.append($i); + $div.append($panel); + + $panel = $('
'); + $panel.addClass('dtp_modal-digits'); + var $d = $(''); + $d.addClass('dtp_modal-digit'); + $d.attr('id', 'd-hh'); + $d.text(lastSelected.format('HH')); + $panel.append($d); + $m = $(''); + $m.addClass('dtp_modal-midle-dig'); + $m.html(':'); + $panel.append($m); + $d = $(''); + $d.addClass('dtp_modal-digit'); + $d.attr('id', 'd-mm'); + $d.text(lastSelected.format('mm')); + $panel.append($d); + $div.append($panel); + + $panel = $('
'); + $panel.addClass('dtp_modal-append'); + $i = $(''); + $i.attr('id', 'angle-down-hour'); + $i.addClass('fa fa-angle-down ico-size-large cursorily hov'); + $panel.append($i); + $m = $(''); + $m.addClass('dtp_modal-midle'); + $panel.append($m); + $i = $(''); + $i.attr('id', 'angle-down-minute'); + $i.addClass('fa fa-angle-down ico-size-large cursorily hov'); + $panel.append($i); + $div.append($panel); + return $div; + } + + function createContent() { + var $c = $('
'); + if (settings.showTime) { + $c.addClass("dtp_modal-content"); + } + else { + $c.addClass("dtp_modal-content-no-time"); + } + var $el = $('
'); + $el.addClass("dtp_modal-title"); + $el.text(settings.title); + $c.append($el); + $el = $('
'); + $el.addClass('dtp_modal-cell-date'); + $el.attr('id', 'field-data'); + $c.append($el); + if (settings.showTime) { + $el = $('
'); + $el.addClass('dtp_modal-cell-time'); + var $a = $('
'); + $a.addClass('dtp_modal-time-block'); + $a.attr('id', 'field-time'); + $el.append($a); + var $line = $('
'); + $line.attr('id', 'time-line'); + $line.addClass('dtp_modal-time-line'); + $line.text(lastSelected.format(settings.dateFormat)); + + $a.append($line); + $a.append(createTimer()); + var $but = $('
'); + $but.addClass('dpt_modal-button'); + $but.text(settings.buttonTitle); + $but.bind('click', close); + $el.append($but); + $c.append($el); + } + return $c; + } + function updateDate() { + if (settings.showTime) { + $('#time-line').text(lastSelected.format(settings.dateFormat)); + } + updateMainElem(); + elem.next().val(selectDate.format(settings.dateFormat)); + if (!settings.showTime) { + $content.remove(); + $win.remove(); + } + } + + function updateMainElem() { + var arrF = settings.dateFormat.split(' '); + if (settings.showTime && arrF.length != 2) { + arrF.length = 2; + arrF[0] = 'DD/MM/YY'; + arrF[1] = 'HH:mm'; + } + var $s = $(''); + $s.text(lastSelected.format(arrF[0])); + elem.empty(); + elem.append($s); + $s = $(''); + $s.addClass('fa fa-calendar ico-size'); + elem.append($s); + if (settings.showTime) { + $s = $(''); + $s.text(lastSelected.format(arrF[1])); + elem.append($s); + $s = $(''); + $s.addClass('fa fa-clock-o ico-size'); + elem.append($s); + } + } + + }); + + }); + + }; + + function copyDate(d) { + return moment(d.toDate()); + } + + function totalMonths(m) { + var r = m.format('YYYY') * 12 + parseInt(m.format('MM')); + return r; + } + +}(jQuery)); +// fa-caret-down \ No newline at end of file diff --git a/templates/admin/input_filter.html b/templates/admin/input_filter.html new file mode 100644 index 0000000..168713b --- /dev/null +++ b/templates/admin/input_filter.html @@ -0,0 +1,15 @@ + + +{% load i18n %} + +

{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}

+
    +
  • +
    + +
    +
  • +
\ No newline at end of file diff --git a/templates/pg/payment_failure.html b/templates/pg/payment_failure.html new file mode 100644 index 0000000..8c6b976 --- /dev/null +++ b/templates/pg/payment_failure.html @@ -0,0 +1,179 @@ +{% load static %} +{% load crispy_forms_tags %} +{% load jalali_tags %} + + + + + + + + Payment Unsuccessful + + + + + + + + + +
+
+ +
+
!
+ {% if message %} +

{{ message }}

+ {% else %} +

پرداخت شما ناموفق بود

+ {% endif %} +
+
+

کد پیگیری تراکنش شما: {{ object.reference_number }}

+

در صورت کسر از موجودی، مبلغ پرداختی طی یک تا 72 ساعت آینده به حساب شما بازگردانده خواهد شد.

+
+ addwin +
+ +
+
+ {% csrf_token %} + {{ form|crispy }} + +
+
+
+
+ + + \ No newline at end of file diff --git a/templates/pg/payment_success.html b/templates/pg/payment_success.html new file mode 100644 index 0000000..b6dd263 --- /dev/null +++ b/templates/pg/payment_success.html @@ -0,0 +1,175 @@ +{% load static %} +{% load crispy_forms_tags %} +{% load jalali_tags %} + + + + + + + Payment Successful + + + + + + + + + +
+
+ +
+
✓
+

پرداخت موفق

+

پرداخت با موفقیت انجام شد.

+
+
+ مقدار پرداخت شده: + {{ object.amount }} تومان +
+ addwin +
+ +
+
+ {% csrf_token %} + {{ form|crispy }} + +
+
+ +
+
+ + + \ No newline at end of file