diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 508f24d..a225337 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index e4ccd77..0f074b3 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 8e87872..219f92b 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/migrations/0019_transaction.py b/core/migrations/0019_transaction.py new file mode 100644 index 0000000..6cb89ed --- /dev/null +++ b/core/migrations/0019_transaction.py @@ -0,0 +1,37 @@ +# Generated by Django 5.2.7 on 2026-01-24 04:34 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0018_remove_appsetting_annual_fee_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Transaction', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Amount')), + ('transaction_type', models.CharField(choices=[('PAYMENT', 'Payment'), ('REFUND', 'Refund')], default='PAYMENT', max_length=20)), + ('status', models.CharField(choices=[('PENDING', 'Pending'), ('COMPLETED', 'Completed'), ('FAILED', 'Failed'), ('CANCELLED', 'Cancelled')], default='COMPLETED', max_length=20)), + ('description', models.TextField(blank=True, verbose_name='Description')), + ('payment_method', models.CharField(blank=True, max_length=100, verbose_name='Payment Method')), + ('reference_number', models.CharField(blank=True, max_length=100, verbose_name='Reference Number')), + ('receipt_number', models.CharField(blank=True, max_length=20, unique=True, verbose_name='Receipt Number')), + ('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='transactions', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Transaction', + 'verbose_name_plural': 'Transactions', + 'ordering': ['-created_at'], + }, + ), + ] diff --git a/core/migrations/__pycache__/0019_transaction.cpython-311.pyc b/core/migrations/__pycache__/0019_transaction.cpython-311.pyc new file mode 100644 index 0000000..9e3ab9d Binary files /dev/null and b/core/migrations/__pycache__/0019_transaction.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index cf5ba9c..08412f8 100644 --- a/core/models.py +++ b/core/models.py @@ -380,4 +380,42 @@ def sync_user_groups(sender, instance, **kwargs): instance.user.groups.remove(*other_groups) # Add user to the correct group - instance.user.groups.add(group) \ No newline at end of file + instance.user.groups.add(group) +class Transaction(models.Model): + TRANSACTION_TYPES = ( + ('PAYMENT', _('Payment')), + ('REFUND', _('Refund')), + ) + STATUS_CHOICES = ( + ('PENDING', _('Pending')), + ('COMPLETED', _('Completed')), + ('FAILED', _('Failed')), + ('CANCELLED', _('Cancelled')), + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='transactions') + amount = models.DecimalField(_('Amount'), max_digits=10, decimal_places=2) + transaction_type = models.CharField(max_length=20, choices=TRANSACTION_TYPES, default='PAYMENT') + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='COMPLETED') + description = models.TextField(_('Description'), blank=True) + payment_method = models.CharField(_('Payment Method'), max_length=100, blank=True) + reference_number = models.CharField(_('Reference Number'), max_length=100, blank=True) + receipt_number = models.CharField(_('Receipt Number'), max_length=20, unique=True, blank=True) + + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + verbose_name = _('Transaction') + verbose_name_plural = _('Transactions') + ordering = ['-created_at'] + + def save(self, *args, **kwargs): + if not self.receipt_number: + # Generate a unique receipt number: REC-YYYYMMDD-XXXX + date_str = timezone.now().strftime('%Y%m%d') + random_str = ''.join(random.choices(string.ascii_uppercase + string.digits, k=4)) + self.receipt_number = f"REC-{date_str}-{random_str}" + super().save(*args, **kwargs) + + def __str__(self): + return f"{self.receipt_number} - {self.user.username} ({self.amount})" diff --git a/core/templates/core/admin_dashboard.html b/core/templates/core/admin_dashboard.html index 9ed20dd..37bf693 100644 --- a/core/templates/core/admin_dashboard.html +++ b/core/templates/core/admin_dashboard.html @@ -303,6 +303,19 @@
{% trans "Manage all platform transactions, payments, and revenue." %}
+| {% trans "User" %} | +{% trans "Role" %} | +{% trans "Receipt #" %} | +{% trans "Date" %} | +{% trans "Amount" %} | +{% trans "Status" %} | +{% trans "Action" %} | +
|---|---|---|---|---|---|---|
|
+ {{ transaction.user.username }} + {{ transaction.user.email }} + |
+ + {{ transaction.user.profile.get_role_display }} + | +{{ transaction.receipt_number }} |
+ {{ transaction.created_at|date:"Y-m-d" }} | +{{ transaction.amount }} | ++ + {{ transaction.get_status_display }} + + | ++ + + {% if transaction.transaction_type == 'PAYMENT' and transaction.status == 'COMPLETED' %} + + + + {% endif %} + + | +
| {% trans "Receipt #" %} | +{% trans "Date" %} | +{% trans "Description" %} | +{% trans "Type" %} | +{% trans "Amount" %} | +{% trans "Status" %} | +{% trans "Action" %} | +
|---|---|---|---|---|---|---|
{{ transaction.receipt_number }} |
+ {{ transaction.created_at|date:"Y-m-d H:i" }} | +{{ transaction.description }} | ++ {% if transaction.transaction_type == 'PAYMENT' %} + {% trans "Payment" %} + {% else %} + {% trans "Refund" %} + {% endif %} + | +{{ transaction.amount }} | ++ + {{ transaction.get_status_display }} + + | ++ + {% trans "Receipt" %} + + | +
{% trans "No transactions found." %}
+
+ {{ app_settings.contact_address|linebreaksbr }}
+ {% trans "Phone:" %} {{ app_settings.contact_phone }}
+ {% trans "Email:" %} {{ app_settings.contact_email }}
+
{% trans "Receipt #" %}: {{ transaction.receipt_number }}
+{% trans "Date" %}: {{ transaction.created_at|date:"Y-m-d" }}
+{{ transaction.user.get_full_name|default:transaction.user.username }}
+{{ transaction.user.email }}
+{{ transaction.user.profile.full_phone_number }}
+{{ transaction.payment_method|default:"N/A" }}
+| {% trans "Description" %} | +{% trans "Total" %} | +
|---|---|
| {{ transaction.description }} | +{{ transaction.amount }} | +
| {% trans "Total Paid" %} | +{{ transaction.amount }} | +
{% trans "Note:" %} {% trans "This is an electronically generated receipt and does not require a physical signature." %}
+{% trans "Thank you for choosing MASAR CARGO!" %}
+ {% if app_settings.registration_number %} +{% trans "CR:" %} {{ app_settings.registration_number }} | {% trans "VAT:" %} {{ app_settings.tax_number }}
+ {% endif %} +{% trans "Manage your shipping offers and active shipments." %}
+{% trans "Manage your shipping offers and active shipments." %}
+