fix date in payment and imporove contract page.

This commit is contained in:
aminhashemi92 2025-09-08 16:55:43 +03:30
parent af40e169ae
commit 204b0aa48e
14 changed files with 295 additions and 74 deletions

View file

@ -7,6 +7,7 @@ from decimal import Decimal
from django.utils import timezone
from django.core.validators import MinValueValidator
from django.conf import settings
from _helpers.utils import jalali_converter2
User = get_user_model()
@ -372,3 +373,6 @@ class Payment(BaseModel):
except Exception:
pass
return result
def jpayment_date(self):
return jalali_converter2(self.payment_date)

View file

@ -150,7 +150,7 @@
<tr>
<td>{% if p.direction == 'in' %}<span class="badge bg-success">دریافتی{% else %}<span class="badge bg-warning text-dark">پرداختی{% endif %}</span></td>
<td>{{ p.amount|floatformat:0|intcomma:False }} تومان</td>
<td>{{ p.payment_date|date:'Y/m/d' }}</td>
<td>{{ p.jpayment_date }}</td>
<td>{{ p.get_payment_method_display }}</td>
<td>{{ p.reference_number|default:'-' }}</td>
<td>
@ -316,11 +316,32 @@
(function initPersianDatePicker(){
if (window.$ && $.fn.persianDatepicker && $('#id_payment_date').length) {
$('#id_payment_date').persianDatepicker({
format: 'YYYY/MM/DD', initialValue: false, autoClose: true, persianDigit: false, observer: true,
format: 'YYYY/MM/DD',
initialValue: false,
autoClose: true,
persianDigit: false,
observer: true,
calendar: { persian: { locale: 'fa', leapYearMode: 'astronomical' } },
onSelect: function(unix){
const g = new window.persianDate(unix).toCalendar('gregorian').format('YYYY-MM-DD');
$('#id_payment_date').attr('data-gregorian', g);
// تبدیل تاریخ شمسی به میلادی برای ارسال به سرور
const gregorianDate = new Date(unix);
const year = gregorianDate.getFullYear();
const month = String(gregorianDate.getMonth() + 1).padStart(2, '0');
const day = String(gregorianDate.getDate()).padStart(2, '0');
const gregorianDateString = `${year}-${month}-${day}`;
// نمایش تاریخ شمسی در فیلد
if (window.persianDate) {
const persianDate = new window.persianDate(unix);
const persianDateString = persianDate.format('YYYY/MM/DD');
$('#id_payment_date').val(persianDateString);
} else {
// اگر persianDate در دسترس نبود، تاریخ میلادی را نمایش بده
$('#id_payment_date').val(gregorianDateString);
}
// ذخیره تاریخ میلادی در فیلد مخفی برای ارسال به سرور
$('#id_payment_date').attr('data-gregorian', gregorianDateString);
}
});
}
@ -328,8 +349,14 @@
function buildForm(){
const fd = new FormData(document.getElementById('formFinalPayment'));
const g = document.getElementById('id_payment_date').getAttribute('data-gregorian');
if (g) { fd.set('payment_date', g); }
// تبدیل تاریخ شمسی به میلادی برای ارسال
const persianDateValue = $('#id_payment_date').val();
const gregorianDateValue = $('#id_payment_date').attr('data-gregorian');
if (persianDateValue && gregorianDateValue) {
fd.set('payment_date', gregorianDateValue);
}
return fd;
}
(function(){

View file

@ -164,7 +164,7 @@
{% for p in payments %}
<tr>
<td>{{ p.amount|floatformat:0|intcomma:False }} تومان</td>
<td>{{ p.payment_date|date:'Y/m/d' }}</td>
<td>{{ p.jpayment_date }}</td>
<td>{{ p.get_payment_method_display }}</td>
<td>{{ p.reference_number|default:'-' }}</td>
<td>
@ -359,6 +359,13 @@
}
const form = document.getElementById('formAddPayment');
const fd = buildFormData(form);
// تبدیل تاریخ شمسی به میلادی برای ارسال
const persianDateValue = $('#id_payment_date').val();
const gregorianDateValue = $('#id_payment_date').attr('data-gregorian');
if (persianDateValue && gregorianDateValue) {
fd.set('payment_date', gregorianDateValue);
}
fetch('{% url "invoices:add_quote_payment" instance.id step.id %}', {
method: 'POST',
body: fd
@ -422,18 +429,24 @@
observer: true,
calendar: { persian: { locale: 'fa', leapYearMode: 'astronomical' } },
onSelect: function(unix) {
// تبدیل تاریخ شمسی به میلادی برای ارسال به سرور
const gregorianDate = new Date(unix);
const year = gregorianDate.getFullYear();
const month = String(gregorianDate.getMonth() + 1).padStart(2, '0');
const day = String(gregorianDate.getDate()).padStart(2, '0');
const gregorianDateString = `${year}-${month}-${day}`;
// نمایش تاریخ شمسی در فیلد
if (window.persianDate) {
const persianDate = new window.persianDate(unix);
const persianDateString = persianDate.format('YYYY/MM/DD');
$('#id_payment_date').val(persianDateString);
} else {
// اگر persianDate در دسترس نبود، تاریخ میلادی را نمایش بده
$('#id_payment_date').val(gregorianDateString);
}
// ذخیره تاریخ میلادی در فیلد مخفی برای ارسال به سرور
$('#id_payment_date').attr('data-gregorian', gregorianDateString);
}
});

View file

@ -323,7 +323,7 @@
{% else %}
{% if next_step %}
<a href="{% url 'processes:step_detail' instance.id next_step.id %}"
class="btn btn-label-primary">
class="btn btn-primary">
مرحله بعد
<i class="bx bx-chevron-left bx-sm me-sm-n2"></i>
</a>

View file

@ -149,7 +149,7 @@
{% endif %}
{% else %}
{% if next_step %}
<a href="{% url 'processes:step_detail' instance.id next_step.id %}" class="btn btn-label-primary">
<a href="{% url 'processes:step_detail' instance.id next_step.id %}" class="btn btn-primary">
مرحله بعد
<i class="bx bx-chevron-left bx-sm me-sm-n2"></i>
</a>

View file

@ -172,18 +172,24 @@ def create_quote(request, instance_id, step_id):
quote.status = 'draft'
quote.save(update_fields=['status'])
if next_step:
next_step_instance = instance.step_instances.filter(step=next_step).first()
if next_step_instance and next_step_instance.status == 'completed':
next_step_instance.status = 'in_progress'
next_step_instance.completed_at = None
next_step_instance.save(update_fields=['status', 'completed_at'])
# Reset ALL subsequent completed steps to in_progress
subsequent_steps = instance.process.steps.filter(order__gt=step.order)
for subsequent_step in subsequent_steps:
subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
if subsequent_step_instance and subsequent_step_instance.status == 'completed':
# Bypass validation by using update() instead of save()
instance.step_instances.filter(step=subsequent_step).update(
status='in_progress',
completed_at=None
)
# Clear previous approvals if the step requires re-approval
try:
next_step_instance.approvals.all().delete()
subsequent_step_instance.approvals.all().delete()
except Exception:
pass
# Set current step to the next step
if next_step:
instance.current_step = next_step
instance.save(update_fields=['current_step'])
@ -524,6 +530,26 @@ def add_quote_payment(request, instance_id, step_id):
si.approvals.all().delete()
except Exception:
pass
# Reset ALL subsequent completed steps to in_progress
try:
subsequent_steps = instance.process.steps.filter(order__gt=step.order)
for subsequent_step in subsequent_steps:
subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
if subsequent_step_instance and subsequent_step_instance.status == 'completed':
# Bypass validation by using update() instead of save()
instance.step_instances.filter(step=subsequent_step).update(
status='in_progress',
completed_at=None
)
# Clear previous approvals if the step requires re-approval
try:
subsequent_step_instance.approvals.all().delete()
except Exception:
pass
except Exception:
pass
# If current step is ahead of this step, reset it back to this step
try:
if instance.current_step and instance.current_step.order > step.order:
@ -572,6 +598,26 @@ def delete_quote_payment(request, instance_id, step_id, payment_id):
si.approvals.all().delete()
except Exception:
pass
# Reset ALL subsequent completed steps to in_progress
try:
subsequent_steps = instance.process.steps.filter(order__gt=step.order)
for subsequent_step in subsequent_steps:
subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
if subsequent_step_instance and subsequent_step_instance.status == 'completed':
# Bypass validation by using update() instead of save()
instance.step_instances.filter(step=subsequent_step).update(
status='in_progress',
completed_at=None
)
# Clear previous approvals if the step requires re-approval
try:
subsequent_step_instance.approvals.all().delete()
except Exception:
pass
except Exception:
pass
# If current step is ahead of this step, reset it back to this step
try:
if instance.current_step and instance.current_step.order > step.order:
@ -1000,6 +1046,25 @@ def add_final_payment(request, instance_id, step_id):
si.save()
except Exception:
pass
# Reset ALL subsequent completed steps to in_progress
try:
subsequent_steps = instance.process.steps.filter(order__gt=step.order)
for subsequent_step in subsequent_steps:
subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
if subsequent_step_instance and subsequent_step_instance.status == 'completed':
# Bypass validation by using update() instead of save()
instance.step_instances.filter(step=subsequent_step).update(
status='in_progress',
completed_at=None
)
# Clear previous approvals if the step requires re-approval
try:
subsequent_step_instance.approvals.all().delete()
except Exception:
pass
except Exception:
pass
return JsonResponse({
'success': True,
'redirect': reverse('invoices:final_settlement_step', args=[instance.id, step_id]),