diff --git a/osinaweb/billing/__pycache__/models.cpython-310.pyc b/osinaweb/billing/__pycache__/models.cpython-310.pyc index 35e332fb..07fab5b8 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/__pycache__/urls.cpython-310.pyc b/osinaweb/billing/__pycache__/urls.cpython-310.pyc index 7def0bd0..ceb6f59d 100644 Binary files a/osinaweb/billing/__pycache__/urls.cpython-310.pyc and b/osinaweb/billing/__pycache__/urls.cpython-310.pyc differ diff --git a/osinaweb/billing/__pycache__/views.cpython-310.pyc b/osinaweb/billing/__pycache__/views.cpython-310.pyc index 41c19eaf..074f9e2c 100644 Binary files a/osinaweb/billing/__pycache__/views.cpython-310.pyc and b/osinaweb/billing/__pycache__/views.cpython-310.pyc differ diff --git a/osinaweb/billing/add/__pycache__/urls.cpython-310.pyc b/osinaweb/billing/add/__pycache__/urls.cpython-310.pyc index 67a26cc4..46a91e1b 100644 Binary files a/osinaweb/billing/add/__pycache__/urls.cpython-310.pyc and b/osinaweb/billing/add/__pycache__/urls.cpython-310.pyc differ diff --git a/osinaweb/billing/add/__pycache__/views.cpython-310.pyc b/osinaweb/billing/add/__pycache__/views.cpython-310.pyc index adcbc06d..c39c66d7 100644 Binary files a/osinaweb/billing/add/__pycache__/views.cpython-310.pyc and b/osinaweb/billing/add/__pycache__/views.cpython-310.pyc differ diff --git a/osinaweb/billing/add/urls.py b/osinaweb/billing/add/urls.py index 96894a68..b5c7f44b 100644 --- a/osinaweb/billing/add/urls.py +++ b/osinaweb/billing/add/urls.py @@ -8,4 +8,5 @@ urlpatterns = [ path('order//', views.add_order, name='addorder'), path('invoice//', views.add_invoice_pdf, name='addinvoice'), + path('service///', views.add_service_in_order, name='addserviceinorder'), ] diff --git a/osinaweb/billing/add/views.py b/osinaweb/billing/add/views.py index 44159426..b6a8a700 100644 --- a/osinaweb/billing/add/views.py +++ b/osinaweb/billing/add/views.py @@ -5,11 +5,12 @@ from django.http import JsonResponse, HttpResponse from django.template.loader import get_template from django.conf import settings import os +from osinacore.decorators import * from django.core.files.base import ContentFile from weasyprint import HTML, CSS - +@staff_login_required def add_product(request, *args, **kwargs): item_types = ProjectType.objects.all().order_by('name') if request.method == 'POST': @@ -38,7 +39,7 @@ def add_product(request, *args, **kwargs): return render(request, 'add_templates/add-product.html', context) - +@staff_login_required def add_service (request, *args, **kwargs): item_types = ProjectType.objects.all().order_by('name') customers = CustomerProfile.objects.all().order_by('user__first_name') @@ -72,38 +73,26 @@ def add_service (request, *args, **kwargs): return render(request, 'add_templates/add-service.html', context) -def add_order (request, customer_id): +@staff_login_required +def add_order(request, customer_id): customer= get_object_or_404(CustomerProfile, id=customer_id) businesses = Business.objects.filter(customer=customer) if request.method == 'POST': - customer_id = request.POST.get('customer') - customer = get_object_or_404(CustomerProfile, id=customer_id) - status = request.POST.get('status') - - customer_id = request.POST.get('customer') - customer = get_object_or_404(CustomerProfile, id=customer_id) - business_id = request.POST.get('business') if business_id: business = get_object_or_404(Business, id=business_id) else: business = None - selected_items = request.POST.getlist('items') order = Order.objects.create( customer=customer, status=status, business=business, ) - - for item_id in selected_items: - item = Item.objects.get(id=item_id) - OrderItem.objects.create(order=order, item=item, purchased_at=datetime.now()) - return redirect('orders') - + return redirect('orderdetails', order_id=order.id) context = { 'customer' : customer, @@ -114,12 +103,27 @@ def add_order (request, customer_id): +@staff_login_required +def add_service_in_order(request, service_id, order_id): + service =get_object_or_404(Item, id=service_id) + order= get_object_or_404(Order, id=order_id) + + order_item = OrderItem.objects.create( + order = order, + item = service, + purchased_at = datetime.now() + ) + order_item.save() + return redirect('orderdetails', order_id=order.id) + + -def add_invoice_pdf(request, order_id): + + +def add_invoice_pdf(request, order_id): order = get_object_or_404(Order, id=order_id) - current_year = str(timezone.now().year)[-2:] last_invoice = Invoice.objects.filter(invoice_number__startswith=current_year).order_by('-invoice_number').first() if last_invoice: @@ -163,21 +167,15 @@ def add_invoice_pdf(request, order_id): presentational_hints=True ) + filename = f'invoice_{invoice.invoice_number}.pdf' + pdf_content = ContentFile(pdf) + invoice.pdf.save(filename, pdf_content, save=True) - # Save PDF to a file - pdf_file_path = os.path.join(settings.MEDIA_ROOT, f'invoice_{invoice.id}.pdf') - with open(pdf_file_path, 'wb') as pdf_file: - pdf_file.write(pdf) - - # Associate PDF file path with the Invoice object - invoice.pdf = pdf_file_path - invoice.save() # Return PDF response = HttpResponse(pdf, content_type='application/pdf') response['Content-Disposition'] = 'attachment; filename="my_pdf.pdf"' - - return response
 + return response diff --git a/osinaweb/billing/delete/__pycache__/urls.cpython-310.pyc b/osinaweb/billing/delete/__pycache__/urls.cpython-310.pyc new file mode 100644 index 00000000..ce36e5e1 Binary files /dev/null and b/osinaweb/billing/delete/__pycache__/urls.cpython-310.pyc differ diff --git a/osinaweb/billing/delete/__pycache__/views.cpython-310.pyc b/osinaweb/billing/delete/__pycache__/views.cpython-310.pyc new file mode 100644 index 00000000..a09ee1bc Binary files /dev/null and b/osinaweb/billing/delete/__pycache__/views.cpython-310.pyc differ diff --git a/osinaweb/billing/delete/urls.py b/osinaweb/billing/delete/urls.py index e69de29b..a81943e8 100644 --- a/osinaweb/billing/delete/urls.py +++ b/osinaweb/billing/delete/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from billing.delete import views + + +urlpatterns = [ + path('orderitem/', views.delete_orderitem, name='deleteorderitem'), +] diff --git a/osinaweb/billing/delete/views.py b/osinaweb/billing/delete/views.py index e69de29b..4e927fbe 100644 --- a/osinaweb/billing/delete/views.py +++ b/osinaweb/billing/delete/views.py @@ -0,0 +1,11 @@ +from django.shortcuts import render, get_object_or_404, redirect +from osinacore.models import * +from billing.models import * +from osinacore.decorators import * + +@staff_login_required +def delete_orderitem(request, orderitem_id): + order_item = get_object_or_404(OrderItem, id=orderitem_id) + order_id = order_item.order.id + order_item.delete() + return redirect('orderdetails', order_id=order_id) diff --git a/osinaweb/billing/templates/details_templates/order-details.html b/osinaweb/billing/templates/details_templates/order-details.html index eb6cdba6..347cde05 100644 --- a/osinaweb/billing/templates/details_templates/order-details.html +++ b/osinaweb/billing/templates/details_templates/order-details.html @@ -9,78 +9,71 @@
-

Order 234443

-

Salim Elliye

+

Order {{order.order_id}}

+

{{order.customer.user.first_name}} {{order.customer.user.last_name}}

-

Completed

+

{{order.status}}

-
-

Services

+ {% if order_items %} +
+

Order Items

-
-
-

Service Name

+ + {% for item in order_items %} +
+
+

{{item.item.title}} +

-
- - - -

Add

+
+

${{item.item.amount}}

+
+ > + + + +

Remove

+
+ {% endfor %}
+ {% endif %} -
-

Products

-
-
-
-

Basic -

- -
-

$0

-

monthly/restaurant

-
-
-
- - - -

Add

-
-
+ {% if services %} +
+

Add Services

+
-
-
-

Standard + {% for service in services %} +

+
+

{{service.title}}

- -
-

$20

-

monthly/restaurant

-
-
+
+

${{service.amount}}

+
+

Add

-
+
+ {% endfor %}
+ {% endif %} +
diff --git a/osinaweb/billing/urls.py b/osinaweb/billing/urls.py index 98fcf974..d21abcda 100644 --- a/osinaweb/billing/urls.py +++ b/osinaweb/billing/urls.py @@ -5,6 +5,7 @@ from billing import views urlpatterns = [ path('add/', include('billing.add.urls')), + path('delete/', include('billing.delete.urls')), # LISTING path('items/', views.items, name='items'), @@ -12,6 +13,6 @@ urlpatterns = [ path('invoices/', views.invoices, name='invoices'), # DETAILS - path('invoice-details//', views.invoice_details, name='invoicedetails'), - path('order-details//', views.order_details, name='orderdetails'), + path('invoices//', views.invoice_details, name='invoicedetails'), + path('orders//', views.order_details, name='orderdetails'), ] diff --git a/osinaweb/billing/views.py b/osinaweb/billing/views.py index 2cac222a..de212bef 100644 --- a/osinaweb/billing/views.py +++ b/osinaweb/billing/views.py @@ -1,9 +1,7 @@ from django.shortcuts import render, get_object_or_404 -from django.utils import timezone -from datetime import timedelta from .models import * -from django.http import JsonResponse, HttpResponse -from django.template.loader import get_template +from django.db.models import Q + # LISTING @@ -49,13 +47,18 @@ def invoice_details(request, order_id): def order_details(request, order_id): order = get_object_or_404(Order, id=order_id) + order_items = OrderItem.objects.filter(order=order).order_by('-id') + order_item_ids = order_items.values_list('item_id', flat=True) + + services = Item.objects.filter(Q(type='Service') & (Q(customer=order.customer) | Q(customer__isnull=True))).exclude(id__in=order_item_ids).order_by('-id') + products = Item.objects.filter(Q(type='Product') & (Q(customer=order.customer) | Q(customer__isnull=True))).exclude(id__in=order_item_ids).order_by('-id') context = { 'order' : order, + 'order_items' : order_items, + 'services': services, } return render(request, 'details_templates/order-details.html', context) - - diff --git a/osinaweb/customercore/__pycache__/custom_context.cpython-310.pyc b/osinaweb/customercore/__pycache__/custom_context.cpython-310.pyc index ffca7fb2..466c37ad 100644 Binary files a/osinaweb/customercore/__pycache__/custom_context.cpython-310.pyc and b/osinaweb/customercore/__pycache__/custom_context.cpython-310.pyc differ diff --git a/osinaweb/customercore/__pycache__/models.cpython-310.pyc b/osinaweb/customercore/__pycache__/models.cpython-310.pyc index d0facf7d..e6eeab32 100644 Binary files a/osinaweb/customercore/__pycache__/models.cpython-310.pyc and b/osinaweb/customercore/__pycache__/models.cpython-310.pyc differ diff --git a/osinaweb/customercore/__pycache__/views.cpython-310.pyc b/osinaweb/customercore/__pycache__/views.cpython-310.pyc index 1de9fe25..58c47d0a 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/custom_context.py b/osinaweb/customercore/custom_context.py index 8e76a44a..ed6b7514 100644 --- a/osinaweb/customercore/custom_context.py +++ b/osinaweb/customercore/custom_context.py @@ -20,4 +20,13 @@ def utilities(request): unread_updates_count += 1 ticket.unread_updates_count = unread_updates_count + last_update = ticket.ticketupdate_set.order_by('-date_added').first() + if last_update: + ticket.last_update_added_by = last_update.added_by.first_name + ticket.last_update_date_added = last_update.date_added + else: + ticket.last_update_added_by = None + ticket.last_update_date_added = None + + return {'active_subscriptions': active_subscriptions, 'customer_open_tickets': customer_open_tickets} diff --git a/osinaweb/customercore/migrations/0009_alter_ticketattachment_ticket.py b/osinaweb/customercore/migrations/0009_alter_ticketattachment_ticket.py new file mode 100644 index 00000000..2aac446e --- /dev/null +++ b/osinaweb/customercore/migrations/0009_alter_ticketattachment_ticket.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.5 on 2024-04-26 08:49 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('customercore', '0008_rename_ticketupdatereadstatus_ticketread'), + ] + + operations = [ + migrations.AlterField( + model_name='ticketattachment', + name='ticket', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='customercore.ticket'), + ), + ] diff --git a/osinaweb/customercore/migrations/__pycache__/0009_alter_ticketattachment_ticket.cpython-310.pyc b/osinaweb/customercore/migrations/__pycache__/0009_alter_ticketattachment_ticket.cpython-310.pyc new file mode 100644 index 00000000..78c75ce8 Binary files /dev/null and b/osinaweb/customercore/migrations/__pycache__/0009_alter_ticketattachment_ticket.cpython-310.pyc differ diff --git a/osinaweb/customercore/models.py b/osinaweb/customercore/models.py index 96e13ad6..b8a2ba3e 100644 --- a/osinaweb/customercore/models.py +++ b/osinaweb/customercore/models.py @@ -65,7 +65,7 @@ class TicketRead(models.Model): read = models.BooleanField(default=False) class TicketAttachment(models.Model): - ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE) + ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, null=True, blank=True) ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE, null=True, blank=True) file = models.FileField() diff --git a/osinaweb/customercore/templates/details_templates/inner-customer-ticket.html b/osinaweb/customercore/templates/details_templates/inner-customer-ticket.html index 6ebe7751..07e58ee4 100644 --- a/osinaweb/customercore/templates/details_templates/inner-customer-ticket.html +++ b/osinaweb/customercore/templates/details_templates/inner-customer-ticket.html @@ -6,33 +6,47 @@
-

Ticket {{ticket.ticket_number}}

+

Ticket #{{ticket.ticket_number}}

+ + {% if last_ticket_status.status == 'Open' %} +
+
+

Opened by {{last_ticket_status.added_by.first_name}} at {{last_ticket_status.date_added}}

+
+ {% elif last_ticket_status.status == 'Working On' %} +
+
+

Updated to 'Working On' by {{last_ticket_status.added_by.first_name}} at {{last_ticket_status.date_added}}

+
+ {% elif last_ticket_status.status == 'Closed' %}
-

Closed by Linode at 20-4-24 16:30

+

Closed by {{last_ticket_status.added_by.first_name}} at {{last_ticket_status.date_added}}

+ {% endif %}
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sagittis, risus id sollicitudin - maximus, diam velit tempus est, pulvinar cursus arcu nisi nec ligula. Nunc eleifend, est eget - lacinia semper, ligula purus scelerisque nunc, a molestie elit magna quis sem. Curabitur eget auctor - mi, vel sodales purus. Nullam odio erat, convallis sit amet feugiat molestie, fermentum sit amet - purus. Proin eget nunc eget risus tristique tempus. Donec egestas bibendum ligula sit amet porta. +

{{ticket.description}}

+ {% if ticket.ticketattachment_set.all %} + {% endif %} +
@@ -92,16 +106,21 @@ {{update.description}}

+ + {% if update.ticketattachment_set.all %} + {% endif %}
@@ -175,7 +194,7 @@
- +
-

My Tickets

+

My Tickets

@@ -85,17 +85,17 @@ -

{{ ticket.last_updated }}

+

{{ticket.last_update_date_added}}

-

{{ ticket.updated_by }}

+

{{ ticket.last_update_added_by }}

-

2

+

{{ticket.unread_updates_count}}

@@ -164,11 +164,11 @@ -

{{ ticket.last_updated }}

+

{{ ticket.last_update_date_added }}

-

{{ ticket.updated_by }}

+

{{ ticket.last_update_added_by }}

{% endfor %} diff --git a/osinaweb/customercore/templates/products/osimenu-plans.html b/osinaweb/customercore/templates/products/osimenu-plans.html index 9964f9e9..1923782e 100644 --- a/osinaweb/customercore/templates/products/osimenu-plans.html +++ b/osinaweb/customercore/templates/products/osimenu-plans.html @@ -13,7 +13,7 @@