emile 1 year ago
parent 5d8243243e
commit 066f2e507b

BIN
.DS_Store vendored

Binary file not shown.

BIN
osinaweb/.DS_Store vendored

Binary file not shown.

Binary file not shown.

@ -0,0 +1,9 @@
from django.urls import path
from billing.add import views
urlpatterns = [
path('product', views.add_product, name='addproduct'),
path('service', views.add_service, name='addservice'),
path('order', views.add_order, name='addorder'),
]

@ -0,0 +1,63 @@
from django.shortcuts import render, get_object_or_404, redirect
from osinacore.models import *
from billing.models import *
from django.http import JsonResponse, HttpResponse
from django.template.loader import get_template
from weasyprint import HTML
def add_product (request, *args, **kwargs):
item_types = ProjectType.objects.all().order_by('name')
if request.method == 'POST':
title = request.POST.get('title')
description = request.POST.get('description')
item_type_id = request.POST.get('item_type')
item_type = get_object_or_404(ProjectType, id=item_type_id)
amount = request.POST.get('amount')
recurring = request.POST.get('recurring')
Item.objects.create(
type='Product',
title=title,
description = description,
item_type = item_type,
amount = amount,
recurring = recurring,
)
return redirect('items')
context = {
'item_types' : item_types,
}
return render(request, 'add_templates/add-product.html', context)
def add_service (request, *args, **kwargs):
context = {
}
return render(request, 'add_templates/add-service.html', context)
def add_order (request, *args, **kwargs):
customers = CustomerProfile.objects.all().order_by('-id')
context = {
'customers': customers,
}
return render(request, 'add_templates/add-order.html', context)
def add_invoice_pdf(request):
template = get_template('details_templates/invoice-details.html')
context = {}
html_string = template.render(context)
pdf = HTML(string=html_string).write_pdf()
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="my_pdf.pdf"'
return response

@ -0,0 +1,22 @@
# Generated by Django 4.2.5 on 2024-04-12 17:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('billing', '0025_remove_item_project_item_customer'),
]
operations = [
migrations.RenameField(
model_name='item',
old_name='type',
new_name='item_type',
),
migrations.RemoveField(
model_name='order',
name='paid',
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-04-12 17:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('billing', '0026_rename_type_item_item_type_remove_order_paid'),
]
operations = [
migrations.AddField(
model_name='item',
name='type',
field=models.CharField(choices=[('Product', 'Product'), ('Service', 'Service')], max_length=200, null=True),
),
]

@ -17,10 +17,11 @@ class Item(models.Model):
('Product', 'Product'), ('Product', 'Product'),
('Service', 'Service'), ('Service', 'Service'),
) )
type = models.CharField(max_length=200, choices=TYPE, null=True)
title = models.CharField(max_length=200) title = models.CharField(max_length=200)
description = models.TextField(blank=True) description = models.TextField(blank=True)
customer = models.ForeignKey(CustomerProfile, null=True, blank=True, on_delete=models.CASCADE) customer = models.ForeignKey(CustomerProfile, null=True, blank=True, on_delete=models.CASCADE)
type = models.ForeignKey(ProjectType, on_delete=models.CASCADE, null=True, blank=True) item_type = models.ForeignKey(ProjectType, on_delete=models.CASCADE, null=True, blank=True)
amount = models.FloatField(null=True) amount = models.FloatField(null=True)
recurring = models.BooleanField(default=False) recurring = models.BooleanField(default=False)
def __str__(self): def __str__(self):
@ -38,7 +39,6 @@ class Order(models.Model):
status = models.CharField(max_length=200, choices=STATUS, default='None') status = models.CharField(max_length=200, choices=STATUS, default='None')
order_id = models.CharField(max_length=100, null=True, blank=True) order_id = models.CharField(max_length=100, null=True, blank=True)
due_date = models.DateField(null=True) due_date = models.DateField(null=True)
paid = models.BooleanField(null=True, default=False)
@property @property
def get_cart_total(self): def get_cart_total(self):
orderitems = self.orderitem_set.all() orderitems = self.orderitem_set.all()

Binary file not shown.

@ -8,55 +8,48 @@
Add Product Add Product
</h1> </h1>
<form method="POST" action="" enctype="multipart/form-data"> <form method="POST" action="{% url 'addproduct' %}" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="w-full flex flex-col gap-5"> <div class="w-full flex flex-col gap-5">
<div class="w-full"> <div class="w-full">
<label class="text-gray-500">Title:</label> <label class="text-gray-500">Title:</label>
<input name="" type="text" <input name="title" type="text"
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1" required> class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1" required>
</div> </div>
<div class="w-full"> <div class="w-full">
<label class="text-gray-500">Description:</label> <label class="text-gray-500">Description:</label>
<textarea name="" type="text" <textarea name="description" type="text"
class="w-full py-1 px-3 border border-gray-300 outline-none rounded-md mt-1 resize-none" class="w-full py-1 px-3 border border-gray-300 outline-none rounded-md mt-1 resize-none"
rows="8" required></textarea> rows="8" required></textarea>
</div> </div>
<div class="w-full"> <div class="w-full">
<label class="text-gray-500">Customer:</label> <label class="text-gray-500">Type:</label>
<select name="type" id="" <select name="item_type" id=""
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md text-gray-500 mt-1">
<option value="">Customer 1</option>
<option value="">Customer 2</option>
</select>
</div>
<div class="w-full">
<label class="text-gray-500">Project Type:</label>
<select name="type" id=""
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md text-gray-500 mt-1" required> class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md text-gray-500 mt-1" required>
<option value="">Type 1</option> <option value="" disabled selected>Select Type</option>
<option value="">Type 2</option> {% for type in item_types %}
<option value="{{type.id}}">{{type.name}}</option>
{% endfor %}
</select> </select>
</div> </div>
<div class="w-full"> <div class="w-full">
<label class="text-gray-500">Amount:</label> <label class="text-gray-500">Amount:</label>
<input name="" type="decimal" <input name="amount" type="decimal"
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1" required> class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1" required>
</div> </div>
<div class="w-full"> <div class="w-full">
<label class="text-gray-500">Recurring:</label> <label class="text-gray-500">Recurring:</label>
<select name="" id="" <select name="recurring" id=""
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md text-gray-500 mt-1" required> class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md text-gray-500 mt-1" required>
<option value="">True</option> <option value="True">True</option>
<option value="">False</option> <option value="False">False</option>
</select> </select>
</div> </div>

@ -21,7 +21,7 @@
</div> </div>
<div class="w-full bg-osiblue h-[50px]"> <div class="w-full bg-osiblue h-[50px]" style="background-color: red;">
</div> </div>
</div> </div>

@ -41,10 +41,6 @@
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap"> class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Title Title
</th> </th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Customer
</th>
<th scope="col" <th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap"> class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Type Type
@ -61,21 +57,18 @@
<!-- TABLE BODY --> <!-- TABLE BODY -->
<tbody class="bg-white divide-y divide-gray-200"> <tbody class="bg-white divide-y divide-gray-200">
{% for product in products %}
<tr> <tr>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300"> <td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-slate-800">Item 1</p> <p class="text-slate-800">{{product.title}}</p>
</td> </td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300"> <td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-slate-800">Nataly</p> <p class="text-slate-800">{{product.item_type}}</p>
</td> </td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300"> <td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-slate-800">Type</p> <p class="text-slate-800">${{product.amount}}</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-slate-800">222</p>
</td> </td>
@ -97,6 +90,7 @@
</div> </div>
</td> </td>
</tr> </tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>

@ -1,36 +1,20 @@
from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf.urls import handler404
from django.conf.urls.static import static
from django.conf import settings
from billing import views from billing import views
urlpatterns = [ urlpatterns = [
path('add/', include('billing.add.urls')),
# LISTING # LISTING
path('items', views.items, name='items'), path('items', views.items, name='items'),
path('orders', views.orders, name='orders'), path('orders', views.orders, name='orders'),
path('invoices', views.invoices, name='invoices'), path('invoices', views.invoices, name='invoices'),
# DETAILS
# ADD
path('add-product', views.add_product, name='addproduct'),
path('add-service', views.add_service, name='addservice'),
path('add-order', views.add_order, name='addorder'),
path('fetch-customer-items/<int:customer_id>/', views.fetch_customer_items, name='fetch_customer_items'),
path('fetch-invoice-details/<int:invoice_id>/', views.fetch_invoice_details, name='fetchinvoicedetails'),
path('invoice-details/<int:invoice_id>/', views.invoice_details, name='invoicedetails'), path('invoice-details/<int:invoice_id>/', views.invoice_details, name='invoicedetails'),
path('generate-pdf/', views.generate_pdf, name='generate-pdf'),
path('fetch-customer-items/<int:customer_id>/', views.fetch_customer_items, name='fetch_customer_items'),
] ]

@ -9,7 +9,9 @@ from weasyprint import HTML
# LISTING # LISTING
def items (request, *args, **kwargs): def items (request, *args, **kwargs):
products = Item.objects.filter(type='Product').order_by('-id')
context = { context = {
'products': products,
} }
return render(request, 'listing_pages/items.html', context) return render(request, 'listing_pages/items.html', context)
@ -32,31 +34,17 @@ def invoices (request, *args, **kwargs):
return render(request, 'listing_pages/invoices.html', context) return render(request, 'listing_pages/invoices.html', context)
#DETAILS
def invoice_details (request, invoice_id):
invoice = get_object_or_404(Invoice, id=invoice_id)
# ADD
def add_product (request, *args, **kwargs):
context = {
}
return render(request, 'add_templates/add-product.html', context)
def add_service (request, *args, **kwargs):
context = { context = {
'invoice' : invoice,
} }
return render(request, 'add_templates/add-service.html', context)
def add_order (request, *args, **kwargs): return render(request, 'invoice-details.html', context)
customers = CustomerProfile.objects.all().order_by('-id')
context = {
'customers': customers,
}
return render(request, 'add_templates/add-order.html', context)
@ -74,51 +62,3 @@ def fetch_customer_items(request, customer_id):
} }
return JsonResponse(data) return JsonResponse(data)
def fetch_invoice_details(request, invoice_id):
invoice = get_object_or_404(Invoice, id=invoice_id)
order = invoice.order
order_items = OrderItem.objects.filter(order=order)
order_total = order.get_cart_total
data = {
'invoice_number': invoice.invoice_number,
'order_id': order.order_id,
'order_total' : order_total,
'date_created': invoice.date_created,
'customer_first_name': order.customer.user.first_name,
'customer_last_name': order.customer.user.last_name,
'customer_email': order.customer.user.email,
'order_items': list(order_items.values()), # Convert QuerySet to list of dictionaries
}
return JsonResponse(data)
def invoice_details (request, invoice_id):
invoice = get_object_or_404(Invoice, id=invoice_id)
context = {
'invoice' : invoice,
}
return render(request, 'invoice-details.html', context)
def generate_pdf(request):
template = get_template('invoice-details.html')
context = {}
html_string = template.render(context)
pdf = HTML(string=html_string).write_pdf()
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="my_pdf.pdf"'
return response

Binary file not shown.

@ -27,6 +27,7 @@ urlpatterns = [
path('businesstype/', views.add_businesstype_modal, name='addbusinesstypemodal'), path('businesstype/', views.add_businesstype_modal, name='addbusinesstypemodal'),
path('reference/', views.add_reference_modal, name='addreferencemodal'), path('reference/', views.add_reference_modal, name='addreferencemodal'),
path('tag/', views.add_tag_modal, name='addtagmodal'), path('tag/', views.add_tag_modal, name='addtagmodal'),
path('reaction/<int:status_id>/<str:emoji>/', views.add_reaction, name='add_reaction'),

@ -616,7 +616,7 @@ def add_reference_modal(request, *args, **kwargs):
@staff_login_required
def add_tag_modal(request, *args, **kwargs): def add_tag_modal(request, *args, **kwargs):
if request.method == 'POST': if request.method == 'POST':
name = request.POST.get('name') name = request.POST.get('name')
@ -633,6 +633,20 @@ def add_tag_modal(request, *args, **kwargs):
@staff_login_required
def add_reaction(request, status_id, emoji):
status = get_object_or_404(Status, pk=status_id)
user = request.user
existing_reaction = Reaction.objects.filter(status=status, user=user).first()
if existing_reaction:
# If the user has already reacted, update the reaction
existing_reaction.emoji = emoji
existing_reaction.save()
return JsonResponse({'message': 'Reaction updated successfully.'})
else:
# If the user hasn't reacted yet, create a new reaction
new_reaction = Reaction.objects.create(status=status, emoji=emoji, user=user)
return JsonResponse({'message': 'Reaction added successfully.'})

@ -21,12 +21,17 @@ class Reference(models.Model):
return f"{parts[2]}-{parts[1]}-{parts[0]}" return f"{parts[2]}-{parts[1]}-{parts[0]}"
class Tag(models.Model): class Tag(models.Model):
name = models.CharField(max_length=50) name = models.CharField(max_length=50)
def __str__(self):
return self.name
class BusinessType(models.Model): class BusinessType(models.Model):
name = models.CharField(max_length=50) name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Business(models.Model): class Business(models.Model):
@ -39,7 +44,9 @@ class Business(models.Model):
website = models.URLField(null=True) website = models.URLField(null=True)
type = models.ForeignKey(BusinessType, on_delete=models.CASCADE, null=True, blank=True) type = models.ForeignKey(BusinessType, on_delete=models.CASCADE, null=True, blank=True)
logo = models.ImageField() logo = models.ImageField()
business_id = models.CharField(max_length=20, null=True, blank=True) # Allow null and blank for initial creation business_id = models.CharField(max_length=20, null=True, blank=True)
def __str__(self):
return self.name
class Meta: class Meta:
verbose_name_plural = u'Businesses' verbose_name_plural = u'Businesses'
def __str__(self): def __str__(self):
@ -102,7 +109,6 @@ class StaffProfile(models.Model):
intern = models.BooleanField(default=False) intern = models.BooleanField(default=False)
active = models.BooleanField(default=True) active = models.BooleanField(default=True)
staff_id = models.CharField(max_length=20, null=True, blank=True) # Allow null and blank for initial creation staff_id = models.CharField(max_length=20, null=True, blank=True) # Allow null and blank for initial creation
def __str__(self): def __str__(self):
return self.user.username return self.user.username
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
@ -119,6 +125,8 @@ class StaffProfile(models.Model):
class ProjectType(models.Model): class ProjectType(models.Model):
name = models.CharField(max_length=50) name = models.CharField(max_length=50)
def __str__(self):
return self.name
@ -138,7 +146,9 @@ class Project(models.Model):
status = models.CharField(max_length=200, choices=STATUS_CHOICES) status = models.CharField(max_length=200, choices=STATUS_CHOICES)
start_date = models.CharField(max_length=200) start_date = models.CharField(max_length=200)
end_date = models.CharField(max_length=200) end_date = models.CharField(max_length=200)
project_id = models.CharField(max_length=20, null=True, blank=True) # Allow null and blank for initial creation project_id = models.CharField(max_length=20, null=True, blank=True)
def __str__(self):
return self.name
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.project_id: if not self.project_id:
# Get the last two digits of the current year # Get the last two digits of the current year
@ -156,6 +166,8 @@ class Milestone(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True) project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
start_date = models.DateField() start_date = models.DateField()
end_date = models.DateField() end_date = models.DateField()
def __str__(self):
return self.title
class Epic(models.Model): class Epic(models.Model):
@ -169,6 +181,8 @@ class Epic(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True) project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
start_date = models.CharField(max_length=200) start_date = models.CharField(max_length=200)
end_date = models.CharField(max_length=200) end_date = models.CharField(max_length=200)
def __str__(self):
return self.title
class ProjectRequirement(models.Model): class ProjectRequirement(models.Model):
@ -176,6 +190,8 @@ class ProjectRequirement(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True) project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
date = models.DateField(null=True, auto_now=True) date = models.DateField(null=True, auto_now=True)
added_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) added_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.content
class ProjectFile(models.Model): class ProjectFile(models.Model):
@ -302,6 +318,8 @@ class Status(models.Model):
date = models.CharField(max_length=40) date = models.CharField(max_length=40)
time = models.CharField(max_length=40) time = models.CharField(max_length=40)
staff = models.ForeignKey(StaffProfile, on_delete=models.CASCADE, null=True,blank=True, related_name='staff') staff = models.ForeignKey(StaffProfile, on_delete=models.CASCADE, null=True,blank=True, related_name='staff')
def __str__(self):
return self.text
class Reaction(models.Model): class Reaction(models.Model):

@ -10,10 +10,9 @@ from django.http import JsonResponse
from .models import Task, Epic from .models import Task, Epic
from django.template.loader import render_to_string from django.template.loader import render_to_string
from .custom_context import calculate_time_ago from .custom_context import calculate_time_ago
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
from django.template.loader import get_template from django.template.loader import get_template
from .decorators import * from .decorators import *
from django.contrib import messages
def login_with_email(request, email, key): def login_with_email(request, email, key):
@ -27,7 +26,7 @@ def login_with_email(request, email, key):
else: else:
pass pass
from django.contrib import messages
# Pages views # Pages views
def signin(request): def signin(request):
if request.user.is_authenticated: if request.user.is_authenticated:

Loading…
Cancel
Save