diff --git a/osinaweb/billing/__pycache__/admin.cpython-310.pyc b/osinaweb/billing/__pycache__/admin.cpython-310.pyc index 341638ea..f8b78a6b 100644 Binary files a/osinaweb/billing/__pycache__/admin.cpython-310.pyc and b/osinaweb/billing/__pycache__/admin.cpython-310.pyc differ diff --git a/osinaweb/billing/__pycache__/models.cpython-310.pyc b/osinaweb/billing/__pycache__/models.cpython-310.pyc index 527391ee..da952b3b 100644 Binary files a/osinaweb/billing/__pycache__/models.cpython-310.pyc and b/osinaweb/billing/__pycache__/models.cpython-310.pyc differ diff --git a/osinaweb/billing/admin.py b/osinaweb/billing/admin.py index f5b4f91c..3005339d 100644 --- a/osinaweb/billing/admin.py +++ b/osinaweb/billing/admin.py @@ -3,11 +3,11 @@ from .models import * # Register your models here. - admin.site.register(RecurringCycle) -admin.site.register(Service) -admin.site.register(ServicePayment) -admin.site.register(PaymentType) +admin.site.register(Item) +admin.site.register(Order) +admin.site.register(OrderItem) admin.site.register(Invoice) +admin.site.register(OrderPayment) +admin.site.register(PaymentType) admin.site.register(Receipt) -admin.site.register(Charge) \ No newline at end of file diff --git a/osinaweb/billing/migrations/0015_order.py b/osinaweb/billing/migrations/0015_order.py new file mode 100644 index 00000000..c170f759 --- /dev/null +++ b/osinaweb/billing/migrations/0015_order.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.5 on 2024-04-09 19:06 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0065_alter_customerprofile_status'), + ('billing', '0014_servicepayment_comment'), + ] + + operations = [ + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('Completed', 'Completed'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), ('Pending', 'Pending'), ('None', 'None')], default='None', max_length=200)), + ('order_id', models.CharField(blank=True, max_length=100, null=True)), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.customerprofile')), + ], + ), + ] diff --git a/osinaweb/billing/migrations/0016_alter_order_status.py b/osinaweb/billing/migrations/0016_alter_order_status.py new file mode 100644 index 00000000..a226d8bc --- /dev/null +++ b/osinaweb/billing/migrations/0016_alter_order_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-04-09 19:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0015_order'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(choices=[('Completed', 'Completed'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), ('None', 'None')], default='Cancelled', max_length=200), + ), + ] diff --git a/osinaweb/billing/migrations/0017_service_order.py b/osinaweb/billing/migrations/0017_service_order.py new file mode 100644 index 00000000..ce24a3f9 --- /dev/null +++ b/osinaweb/billing/migrations/0017_service_order.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.5 on 2024-04-09 19:10 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0016_alter_order_status'), + ] + + operations = [ + migrations.AddField( + model_name='service', + name='order', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.order'), + ), + ] diff --git a/osinaweb/billing/migrations/0018_alter_order_status.py b/osinaweb/billing/migrations/0018_alter_order_status.py new file mode 100644 index 00000000..9e0b7c5c --- /dev/null +++ b/osinaweb/billing/migrations/0018_alter_order_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-04-09 19:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0017_service_order'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(choices=[('Completed', 'Completed'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), ('None', 'None')], default='None', max_length=200), + ), + ] diff --git a/osinaweb/billing/migrations/0019_item_orderpayment_remove_service_customer_and_more.py b/osinaweb/billing/migrations/0019_item_orderpayment_remove_service_customer_and_more.py new file mode 100644 index 00000000..7043fd64 --- /dev/null +++ b/osinaweb/billing/migrations/0019_item_orderpayment_remove_service_customer_and_more.py @@ -0,0 +1,111 @@ +# Generated by Django 4.2.5 on 2024-04-09 20:21 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0065_alter_customerprofile_status'), + ('billing', '0018_alter_order_status'), + ] + + operations = [ + migrations.CreateModel( + name='Item', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('description', models.TextField(blank=True)), + ('amount', models.FloatField(null=True)), + ('recurring', models.BooleanField(default=False)), + ('created_at', models.DateField(blank=True, null=True)), + ('cancelled_at', models.DateField(blank=True, null=True)), + ('active', models.BooleanField(default=True, null=True)), + ('project', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='osinacore.project')), + ('recurring_cycle', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.recurringcycle')), + ('type', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='osinacore.projecttype')), + ], + ), + migrations.CreateModel( + name='OrderPayment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=3, max_digits=5)), + ('date_paid', models.DateField()), + ('comment', models.TextField(blank=True, null=True)), + ], + ), + migrations.RemoveField( + model_name='service', + name='customer', + ), + migrations.RemoveField( + model_name='service', + name='order', + ), + migrations.RemoveField( + model_name='service', + name='project', + ), + migrations.RemoveField( + model_name='service', + name='recurring_cycle', + ), + migrations.RemoveField( + model_name='service', + name='type', + ), + migrations.RemoveField( + model_name='servicepayment', + name='charge', + ), + migrations.RemoveField( + model_name='servicepayment', + name='type', + ), + migrations.RemoveField( + model_name='invoice', + name='charge', + ), + migrations.AddField( + model_name='invoice', + name='order', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.order'), + ), + migrations.AddField( + model_name='order', + name='due_date', + field=models.DateField(null=True), + ), + migrations.AddField( + model_name='order', + name='paid', + field=models.BooleanField(default=False, null=True), + ), + migrations.DeleteModel( + name='Charge', + ), + migrations.DeleteModel( + name='Service', + ), + migrations.DeleteModel( + name='ServicePayment', + ), + migrations.AddField( + model_name='orderpayment', + name='order', + field=models.ManyToManyField(null=True, to='billing.order'), + ), + migrations.AddField( + model_name='orderpayment', + name='type', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.paymenttype'), + ), + migrations.AlterField( + model_name='receipt', + name='payment', + field=models.ManyToManyField(to='billing.orderpayment'), + ), + ] diff --git a/osinaweb/billing/migrations/0020_orderitem.py b/osinaweb/billing/migrations/0020_orderitem.py new file mode 100644 index 00000000..704a1a49 --- /dev/null +++ b/osinaweb/billing/migrations/0020_orderitem.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.5 on 2024-04-09 20:23 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0019_item_orderpayment_remove_service_customer_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='OrderItem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='billing.item')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='billing.order')), + ], + ), + ] diff --git a/osinaweb/billing/migrations/0021_remove_item_recurring_cycle_and_more.py b/osinaweb/billing/migrations/0021_remove_item_recurring_cycle_and_more.py new file mode 100644 index 00000000..4bf3858e --- /dev/null +++ b/osinaweb/billing/migrations/0021_remove_item_recurring_cycle_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.5 on 2024-04-09 20:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0020_orderitem'), + ] + + operations = [ + migrations.RemoveField( + model_name='item', + name='recurring_cycle', + ), + migrations.AddField( + model_name='orderitem', + name='recurring_cycle', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.recurringcycle'), + ), + ] diff --git a/osinaweb/billing/migrations/__pycache__/0015_order.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0015_order.cpython-310.pyc new file mode 100644 index 00000000..5aa762ec Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0015_order.cpython-310.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0016_alter_order_status.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0016_alter_order_status.cpython-310.pyc new file mode 100644 index 00000000..82822267 Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0016_alter_order_status.cpython-310.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0017_service_order.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0017_service_order.cpython-310.pyc new file mode 100644 index 00000000..c5cf3379 Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0017_service_order.cpython-310.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0018_alter_order_status.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0018_alter_order_status.cpython-310.pyc new file mode 100644 index 00000000..141ad33c Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0018_alter_order_status.cpython-310.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0019_item_orderpayment_remove_service_customer_and_more.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0019_item_orderpayment_remove_service_customer_and_more.cpython-310.pyc new file mode 100644 index 00000000..fdb7cecc Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0019_item_orderpayment_remove_service_customer_and_more.cpython-310.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0020_orderitem.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0020_orderitem.cpython-310.pyc new file mode 100644 index 00000000..0e98e6e5 Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0020_orderitem.cpython-310.pyc differ diff --git a/osinaweb/billing/migrations/__pycache__/0021_remove_item_recurring_cycle_and_more.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0021_remove_item_recurring_cycle_and_more.cpython-310.pyc new file mode 100644 index 00000000..46f07592 Binary files /dev/null and b/osinaweb/billing/migrations/__pycache__/0021_remove_item_recurring_cycle_and_more.cpython-310.pyc differ diff --git a/osinaweb/billing/models.py b/osinaweb/billing/models.py index 78c80e16..12065f13 100644 --- a/osinaweb/billing/models.py +++ b/osinaweb/billing/models.py @@ -9,29 +9,58 @@ class RecurringCycle(models.Model): return f"{self.months} months" -class Service(models.Model): + +class Item(models.Model): + TYPE = ( + ('Product', 'Product'), + ('Service', 'Service'), + ) title = models.CharField(max_length=200) description = models.TextField(blank=True) - customer = models.ForeignKey(CustomerProfile, on_delete=models.CASCADE) project = models.ForeignKey(Project, blank=True, on_delete=models.CASCADE) type = models.ForeignKey(ProjectType, on_delete=models.CASCADE, blank=True) - amount = models.DecimalField(decimal_places=3, max_digits = 5) + amount = models.FloatField(null=True) recurring = models.BooleanField(default=False) - recurring_cycle = models.ForeignKey(RecurringCycle, on_delete=models.SET_NULL, null=True, blank=True) - created_at = models.DateField(null=True) + created_at = models.DateField(null=True, blank=True) cancelled_at = models.DateField(blank=True, null=True) active = models.BooleanField(default=True, null=True) def __str__(self): return self.title - -class Charge(models.Model): - service = models.ForeignKey(Service, on_delete=models.CASCADE) - amount = models.DecimalField(decimal_places=3, max_digits = 5, null=True) - due_date = models.DateField() - is_paid = models.BooleanField(default=False) + +class Order(models.Model): + STATUS = ( + ('Completed', 'Completed'), + ('Failed', 'Failed'), + ('Cancelled', 'Cancelled'), + ('None', 'None'), + ) + customer = models.ForeignKey(CustomerProfile, on_delete=models.CASCADE) + status = models.CharField(max_length=200, choices=STATUS, default='None') + order_id = models.CharField(max_length=100, null=True, blank=True) + due_date = models.DateField(null=True) + paid = models.BooleanField(null=True, default=False) + def save(self, *args, **kwargs): + if not self.order_id: + order_count = Order.objects.filter(customer=self.customer).count() + order_count_str = str(order_count).zfill(4) + self.order_id = f"{self.customer.customer_id}-{order_count_str}" + super(Order, self).save(*args, **kwargs) + + +class OrderItem(models.Model): + order = models.ForeignKey(Order, on_delete=models.CASCADE) + item = models.ForeignKey(Item, on_delete=models.CASCADE) + recurring_cycle = models.ForeignKey(RecurringCycle, on_delete=models.SET_NULL, null=True, blank=True) + + + +class Invoice(models.Model): + invoice_number = models.CharField(max_length=100) + order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True) + date_created = models.DateField() def __str__(self): - return f"{self.service}'s charge due {self.due_date}" + return self.invoice_number class PaymentType(models.Model): @@ -40,27 +69,18 @@ class PaymentType(models.Model): return self.name -class ServicePayment(models.Model): - charge = models.ManyToManyField(Charge, null=True) +class OrderPayment(models.Model): + order = models.ManyToManyField(Order, null=True) amount = models.DecimalField(decimal_places=3, max_digits = 5) date_paid = models.DateField() type = models.ForeignKey(PaymentType, null=True, on_delete=models.SET_NULL) comment = models.TextField(null=True, blank=True) def __str__(self): - return f"Payment for {self.charge}" - - -class Invoice(models.Model): - invoice_number = models.CharField(max_length=100) - charge = models.ManyToManyField(Charge) - date_created = models.DateField() - def __str__(self): - return self.invoice_number - + return f"Payment for {self.order}" class Receipt(models.Model): receipt_number = models.CharField(max_length=100) - payment = models.ManyToManyField(ServicePayment) + payment = models.ManyToManyField(OrderPayment) date_generated = models.DateField() def __str__(self): return self.receipt_number \ No newline at end of file diff --git a/osinaweb/customercore/__pycache__/views.cpython-310.pyc b/osinaweb/customercore/__pycache__/views.cpython-310.pyc index 608b59e8..1368fd28 100644 Binary files a/osinaweb/customercore/__pycache__/views.cpython-310.pyc and b/osinaweb/customercore/__pycache__/views.cpython-310.pyc differ diff --git a/osinaweb/customercore/views.py b/osinaweb/customercore/views.py index fceb5897..8185d868 100644 --- a/osinaweb/customercore/views.py +++ b/osinaweb/customercore/views.py @@ -3,6 +3,7 @@ from django.contrib.auth.decorators import login_required from django.http import JsonResponse import requests from .decorators import * +from billing.models import * import uuid import base64 @@ -56,9 +57,10 @@ def initiate_checkout(request): merchant_id = 'TEST06127800' merchant_name = 'Ositcom Sal' - order_id = str(uuid.uuid4())[:8] - - + customer = request.user.customerprofile + order = Order.objects.create(status='None', customer=customer) + order.save() + order_id = order.order_id payload = { 'apiOperation': 'INITIATE_CHECKOUT', @@ -98,6 +100,7 @@ def initiate_checkout(request): def check_order_status(request, merchant_id, order_id): api_password = '37846250a67c70e7fe9f82cf6ca81f93' url = f"https://creditlibanais-netcommerce.gateway.mastercard.com/api/rest/version/78/merchant/{merchant_id}/order/{order_id}" + order = Order.objects.get(order_id=order_id) headers = { 'Content-Type': 'application/json', @@ -109,7 +112,10 @@ def check_order_status(request, merchant_id, order_id): if response.status_code == 200: order_details = response.json() - return JsonResponse(order_details) + if order_details.get('result') == 'SUCCESS': + order.status = 'Completed' + order.save() + return JsonResponse(order_details) else: error_message = 'Failed to retrieve order details: ' + response.text return JsonResponse({'error': error_message}, status=500) diff --git a/osinaweb/db.sqlite3 b/osinaweb/db.sqlite3 index efc16a28..088ee345 100644 Binary files a/osinaweb/db.sqlite3 and b/osinaweb/db.sqlite3 differ