update tasks

This commit is contained in:
ismail 2025-05-04 15:57:24 +03:00
parent 44f540bae5
commit e10b5f64ea
16 changed files with 794 additions and 706 deletions

View File

@ -0,0 +1,17 @@
from django.core.management.base import BaseCommand
from django.core.mail import send_mail
from allauth.account.models import EmailConfirmation
from inventory.tasks import send_email
from django.contrib.auth import get_user_model
User = get_user_model()
class Command(BaseCommand):
def handle(self, *args, **kwargs):
user = User.objects.last()
print(user.email)
# 2. Force email confirmation
# email = user.emailaddress_set.first()
confirmation = EmailConfirmation.create(user.email)
confirmation.send()

View File

@ -0,0 +1,12 @@
from django.core.management.base import BaseCommand
# from background_task.models import Task
# from background_task import background
# from django_q.tasks import async_task
from inventory.tasks import send_email
class Command(BaseCommand):
def handle(self, *args, **kwargs):
send_email('ismail.mosa@gmail.com', 'teset1@gmail.com', 'test', 'test')

View File

@ -1,7 +1,9 @@
# Generated by Django 5.1.7 on 2025-03-27 20:55
# Generated by Django 5.1.7 on 2025-05-04 10:42
import datetime
import django.core.validators
import django.db.models.deletion
import django.utils.timezone
import inventory.mixins
import inventory.models
import phonenumber_field.modelfields
@ -442,7 +444,7 @@ class Migration(migrations.Migration):
name='Email',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField()),
('object_id', models.UUIDField()),
('from_email', models.TextField(blank=True, null=True, verbose_name='From Email')),
('to_email', models.TextField(blank=True, null=True, verbose_name='To Email')),
('subject', models.TextField(blank=True, null=True, verbose_name='Subject')),
@ -491,7 +493,7 @@ class Migration(migrations.Migration):
name='Notes',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.PositiveIntegerField()),
('object_id', models.UUIDField()),
('note', models.TextField(verbose_name='Note')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
@ -698,7 +700,7 @@ class Migration(migrations.Migration):
('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
('email', models.EmailField(max_length=255, verbose_name='Email Address')),
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
('address', models.CharField(max_length=200, verbose_name='Address')),
('logo', models.ImageField(blank=True, null=True, upload_to='logos/vendors', verbose_name='Logo')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')),
@ -751,4 +753,33 @@ class Migration(migrations.Migration):
'unique_together': {('car', 'exterior', 'interior')},
},
),
migrations.CreateModel(
name='PaymentHistory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user_data', models.JSONField(blank=True, null=True)),
('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0.01)])),
('currency', models.CharField(default='SAR', max_length=3)),
('payment_date', models.DateTimeField(default=django.utils.timezone.now)),
('status', models.CharField(choices=[('initiated', 'initiated'), ('pending', 'Pending'), ('completed', 'Completed'), ('paid', 'Paid'), ('failed', 'Failed'), ('refunded', 'Refunded'), ('cancelled', 'Cancelled')], default='pending', max_length=10)),
('payment_method', models.CharField(choices=[('credit_card', 'Credit Card'), ('debit_card', 'Debit Card'), ('paypal', 'PayPal'), ('bank_transfer', 'Bank Transfer'), ('crypto', 'Cryptocurrency'), ('other', 'Other')], max_length=20)),
('transaction_id', models.CharField(blank=True, max_length=100, null=True, unique=True)),
('invoice_number', models.CharField(blank=True, max_length=50, null=True)),
('order_reference', models.CharField(blank=True, max_length=100, null=True)),
('gateway_response', models.JSONField(blank=True, null=True)),
('gateway_name', models.CharField(blank=True, max_length=50, null=True)),
('description', models.TextField(blank=True, null=True)),
('is_recurring', models.BooleanField(default=False)),
('billing_email', models.EmailField(blank=True, max_length=254, null=True)),
('billing_address', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'Payment Histories',
'ordering': ['-payment_date'],
'indexes': [models.Index(fields=['transaction_id'], name='inventory_p_transac_9469f3_idx'), models.Index(fields=['user'], name='inventory_p_user_id_c31626_idx'), models.Index(fields=['status'], name='inventory_p_status_abcb77_idx'), models.Index(fields=['payment_date'], name='inventory_p_payment_b3068c_idx')],
},
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 5.1.7 on 2025-04-24 16:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='notes',
name='object_id',
field=models.PositiveBigIntegerField(),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 5.1.7 on 2025-04-24 16:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0002_alter_notes_object_id'),
]
operations = [
migrations.AlterField(
model_name='email',
name='object_id',
field=models.PositiveBigIntegerField(),
),
]

View File

@ -1,23 +0,0 @@
# Generated by Django 5.1.7 on 2025-04-24 16:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0003_alter_email_object_id'),
]
operations = [
migrations.AlterField(
model_name='email',
name='object_id',
field=models.UUIDField(),
),
migrations.AlterField(
model_name='notes',
name='object_id',
field=models.UUIDField(),
),
]

View File

@ -1,23 +0,0 @@
# Generated by Django 5.1.7 on 2025-04-24 16:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0004_alter_email_object_id_alter_notes_object_id'),
]
operations = [
migrations.AlterField(
model_name='email',
name='object_id',
field=models.PositiveIntegerField(),
),
migrations.AlterField(
model_name='notes',
name='object_id',
field=models.PositiveIntegerField(),
),
]

View File

@ -1,23 +0,0 @@
# Generated by Django 5.1.7 on 2025-04-24 17:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0005_alter_email_object_id_alter_notes_object_id'),
]
operations = [
migrations.AlterField(
model_name='email',
name='object_id',
field=models.UUIDField(),
),
migrations.AlterField(
model_name='notes',
name='object_id',
field=models.UUIDField(),
),
]

View File

@ -1,47 +0,0 @@
# Generated by Django 5.1.7 on 2025-04-29 13:33
import django.core.validators
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0006_alter_email_object_id_alter_notes_object_id'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='PaymentHistory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user_data', models.JSONField(blank=True, null=True)),
('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0.01)])),
('currency', models.CharField(default='SAR', max_length=3)),
('payment_date', models.DateTimeField(default=django.utils.timezone.now)),
('status', models.CharField(choices=[('initiated', 'initiated'), ('pending', 'Pending'), ('completed', 'Completed'), ('paid', 'Paid'), ('failed', 'Failed'), ('refunded', 'Refunded'), ('cancelled', 'Cancelled')], default='pending', max_length=10)),
('payment_method', models.CharField(choices=[('credit_card', 'Credit Card'), ('debit_card', 'Debit Card'), ('paypal', 'PayPal'), ('bank_transfer', 'Bank Transfer'), ('crypto', 'Cryptocurrency'), ('other', 'Other')], max_length=20)),
('transaction_id', models.CharField(blank=True, max_length=100, null=True, unique=True)),
('invoice_number', models.CharField(blank=True, max_length=50, null=True)),
('order_reference', models.CharField(blank=True, max_length=100, null=True)),
('gateway_response', models.JSONField(blank=True, null=True)),
('gateway_name', models.CharField(blank=True, max_length=50, null=True)),
('description', models.TextField(blank=True, null=True)),
('is_recurring', models.BooleanField(default=False)),
('billing_email', models.EmailField(blank=True, max_length=254, null=True)),
('billing_address', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'Payment Histories',
'ordering': ['-payment_date'],
'indexes': [models.Index(fields=['transaction_id'], name='inventory_p_transac_9469f3_idx'), models.Index(fields=['user'], name='inventory_p_user_id_c31626_idx'), models.Index(fields=['status'], name='inventory_p_status_abcb77_idx'), models.Index(fields=['payment_date'], name='inventory_p_payment_b3068c_idx')],
},
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 5.1.7 on 2025-05-01 13:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0007_paymenthistory'),
]
operations = [
migrations.AlterField(
model_name='vendor',
name='address',
field=models.CharField(default='', max_length=200, verbose_name='Address'),
preserve_default=False,
),
]

View File

@ -145,7 +145,8 @@ def create_ledger_entity(sender, instance, created, **kwargs):
# Create COA accounts, background task
create_coa_accounts(instance.pk)
create_settings(instance.pk)
# create_settings(instance.pk)
# create_accounts_for_make(instance.pk)
@receiver(post_save, sender=models.Dealer)

File diff suppressed because it is too large Load Diff

View File

@ -43,12 +43,16 @@ urlpatterns = [
# template_name="account/request_login_code.html"
# ),
# ),
# Tasks
path('tasks/', views.task_list, name='task_list'),
# path('tasks/<int:task_id>/detail/', views.task_detail, name='task_detail'),
# Dashboards
# path("user/<int:pk>/settings/", views.UserSettingsView.as_view(), name="user_settings"),
path("pricing/", views.pricing_page, name="pricing_page"),
path("submit_plan/", views.submit_plan, name="submit_plan"),
path('payment-callback/', views.payment_callback, name='payment_callback'),
#
#
path("dealers/<int:pk>/settings/", views.DealerSettingsView, name="dealer_settings"),
path("dealers/assign-car-makes/", views.assign_car_makes, name="assign_car_makes"),
path("dashboards/manager/", views.ManagerDashboard.as_view(), name="manager_dashboard"),

View File

@ -14,6 +14,7 @@ from urllib.parse import urlparse, urlunparse
#####################################################################
from background_task.models import Task
from django.db.models.deletion import RestrictedError
# Django
@ -7702,3 +7703,16 @@ def payment_callback(request):
history.save()
message = request.GET.get('message')
return render(request, "payment_failed.html", {"message": message})
# Background Tasks
def task_list(request):
# Get all tasks ordered by creation time
tasks = Task.objects.all()
# Add pagination
paginator = Paginator(tasks, 10) # Show 10 tasks per page
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'tasks/task_list.html', {'page_obj': page_obj})

View File

@ -0,0 +1,158 @@
{% extends "base.html" %}
{% load static i18n %}
{% block content %}
<div class="container mt-4">
<h1 class="mb-4">Background Tasks</h1>
<!-- Task Count and Refresh Button -->
<div class="d-flex justify-content-between mb-3">
<div>
<span class="badge bg-primary">Total Tasks: {{ page_obj.paginator.count }}</span>
</div>
<div>
<a href="{% url 'task_list' %}" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-arrow-clockwise"></i> Refresh
</a>
</div>
</div>
<!-- Task Table -->
<div class="table-responsive">
<table class="table table-sm fs-9 mb-0">
<thead>
<tr>
<th>ID</th>
<th>Task Name</th>
<th>Status</th>
<th>Created At</th>
<th>Scheduled Run</th>
<th>Last Run</th>
<th>Attempts</th>
<th>Priority</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for task in page_obj %}
<tr>
<td>{{ task.id }}</td>
<td>
<span class="d-inline-block text-truncate" style="max-width: 150px;">
{{ task.task_name }}
</span>
</td>
<td>
{% if task.locked_at %}
<span class="badge bg-warning text-dark">Running</span>
{% elif task.last_run_at and task.has_error %}
<span class="badge bg-danger">Failed</span>
{% elif task.last_run_at %}
<span class="badge bg-success">Completed</span>
{% else %}
<span class="badge bg-info text-dark">Pending</span>
{% endif %}
</td>
<td>{{ task.created_at|date:"Y-m-d H:i" }}</td>
<td>{{ task.run_at|date:"Y-m-d H:i" }}</td>
<td>
{% if task.last_run_at %}
{{ task.last_run_at|date:"Y-m-d H:i" }}
{% else %}
-
{% endif %}
</td>
<td>{{ task.attempts }}</td>
<td>{{ task.priority }}</td>
{% comment %} <td>
<div class="btn-group btn-group-sm">
<button class="btn btn-sm btn-phoenix-primary" data-bs-toggle="modal"
data-bs-target="#taskDetailModal"
data-task-id="{{ task.id }}">
<i class="bi bi-eye"></i>
</button>
</div>
</td> {% endcomment %}
</tr>
{% empty %}
<tr>
<td colspan="9" class="text-center">No tasks found</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if page_obj.paginator.num_pages > 1 %}
<nav aria-label="Task pagination">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1">&laquo; First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
</li>
{% endif %}
<li class="page-item active">
<span class="page-link">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last &raquo;</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
<!-- Task Detail Modal -->
<div class="modal fade" id="taskDetailModal" tabindex="-1" aria-labelledby="taskDetailModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="taskDetailModalLabel">Task Details</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="taskDetailsContent">
Loading task details...
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Load task details in modal
document.getElementById('taskDetailModal').addEventListener('show.bs.modal', function (event) {
const button = event.relatedTarget;
const taskId = button.getAttribute('data-task-id');
const modal = this;
fetch(`/tasks/${taskId}/detail/`)
.then(response => response.text())
.then(html => {
modal.querySelector('#taskDetailsContent').innerHTML = html;
})
.catch(error => {
modal.querySelector('#taskDetailsContent').innerHTML =
`<div class="alert alert-danger">Error loading task details: ${error}</div>`;
});
});
</script>
{% endblock %}