New changes + introducing addressbook and billimg
parent
06d6460600
commit
db1bfa95ff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
from .models import *
|
||||
|
||||
# Register your models here.
|
||||
|
||||
admin.site.register(Group)
|
||||
admin.site.register(Country)
|
||||
admin.site.register(AddressBook)
|
||||
admin.site.register(Contact)
|
||||
admin.site.register(SocialMedia)
|
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AddressbookConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'addressbook'
|
@ -0,0 +1,21 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 18:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Group',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 18:41
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('addressbook', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Country',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('code', models.CharField(max_length=5)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AddressBook',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(max_length=100)),
|
||||
('middle_name', models.CharField(blank=True, max_length=100, null=True)),
|
||||
('last_name', models.CharField(max_length=100)),
|
||||
('country', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='addressbook.country')),
|
||||
('group', models.ManyToManyField(blank=True, to='addressbook.group')),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 18:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('addressbook', '0002_country_addressbook'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Contact',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', models.CharField(choices=[('Email', 'Email'), ('Mobile', 'Mobile'), ('Phone', 'Phone'), ('Whatsapp', 'Whatsapp')], max_length=20)),
|
||||
('contact', models.CharField(max_length=100)),
|
||||
('addressbook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='addressbook.addressbook')),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 18:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('addressbook', '0003_contact'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SocialMedia',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', models.CharField(choices=[('Facebook', 'Facebook'), ('Instagram', 'Instagram'), ('X', 'X'), ('Tiktok', 'Tiktok'), ('Linkedin', 'Linkedin'), ('Youtube', 'Youtube')], max_length=20)),
|
||||
('username', models.CharField(blank=True, max_length=50)),
|
||||
('link', models.URLField(blank=True)),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 18:54
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('addressbook', '0004_socialmedia'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='socialmedia',
|
||||
name='addressbook',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='addressbook.addressbook'),
|
||||
),
|
||||
]
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,49 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
|
||||
|
||||
class Group(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Country(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
code = models.CharField(max_length=5)
|
||||
|
||||
class AddressBook(models.Model):
|
||||
first_name = models.CharField(max_length=100)
|
||||
middle_name = models.CharField(max_length=100, blank=True, null=True)
|
||||
last_name = models.CharField(max_length=100)
|
||||
country = models.ForeignKey(Country, null=True, on_delete=models.SET_NULL)
|
||||
group = models.ManyToManyField(Group, blank=True)
|
||||
|
||||
|
||||
class Contact(models.Model):
|
||||
TYPE_CHOICES = (
|
||||
('Email', 'Email'),
|
||||
('Mobile', 'Mobile'),
|
||||
('Phone', 'Phone'),
|
||||
('Whatsapp', 'Whatsapp'),
|
||||
)
|
||||
type = models.CharField(max_length=20, choices=TYPE_CHOICES)
|
||||
contact = models.CharField(max_length=100)
|
||||
addressbook = models.ForeignKey(AddressBook, on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class SocialMedia(models.Model):
|
||||
TYPE_CHOICES = (
|
||||
('Facebook', 'Facebook'),
|
||||
('Instagram', 'Instagram'),
|
||||
('X', 'X'),
|
||||
('Tiktok', 'Tiktok'),
|
||||
('Linkedin', 'Linkedin'),
|
||||
('Youtube', 'Youtube'),
|
||||
)
|
||||
type = models.CharField(max_length=20, choices=TYPE_CHOICES)
|
||||
username = models.CharField(max_length=50, blank=True)
|
||||
link = models.URLField(blank=True)
|
||||
addressbook = models.ForeignKey(AddressBook, on_delete=models.CASCADE, null=True)
|
||||
|
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,13 @@
|
||||
from django.contrib import admin
|
||||
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(Invoice)
|
||||
admin.site.register(Receipt)
|
||||
admin.site.register(Charge)
|
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BillingConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'billing'
|
@ -0,0 +1,36 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('osinacore', '0058_remove_point_total_time_hours_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='RecurringCycle',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('months', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Service',
|
||||
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.DecimalField(decimal_places=3, max_digits=5)),
|
||||
('recurring', models.BooleanField(default=False)),
|
||||
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.customerprofile')),
|
||||
('project', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='osinacore.project')),
|
||||
('type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.projecttype')),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='service',
|
||||
name='active',
|
||||
field=models.BooleanField(default=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='service',
|
||||
name='cancelled_at',
|
||||
field=models.DateField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='service',
|
||||
name='created_at',
|
||||
field=models.DateField(auto_now=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='service',
|
||||
name='due_at',
|
||||
field=models.DateField(auto_now=True, null=True),
|
||||
),
|
||||
]
|
@ -0,0 +1,31 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:35
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0002_service_active_service_cancelled_at_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Payment',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ServicePayment',
|
||||
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()),
|
||||
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='billing.service')),
|
||||
('type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.payment')),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:35
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0003_payment_servicepayment'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='Payment',
|
||||
new_name='PaymentType',
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:39
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0004_rename_payment_paymenttype'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='service',
|
||||
name='recurring_cycle',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.recurringcycle'),
|
||||
),
|
||||
]
|
@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0005_service_recurring_cycle'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='service',
|
||||
name='cancelled_at',
|
||||
field=models.DateField(blank=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='service',
|
||||
name='created_at',
|
||||
field=models.DateField(null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='service',
|
||||
name='due_at',
|
||||
field=models.DateField(blank=True, null=True),
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:41
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0006_alter_service_cancelled_at_alter_service_created_at_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='service',
|
||||
name='recurring_cycle',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.recurringcycle'),
|
||||
),
|
||||
]
|
@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:43
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('osinacore', '0058_remove_point_total_time_hours_and_more'),
|
||||
('billing', '0007_alter_service_recurring_cycle'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='service',
|
||||
name='type',
|
||||
field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='osinacore.projecttype'),
|
||||
),
|
||||
]
|
@ -0,0 +1,36 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 19:57
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0008_alter_service_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='service',
|
||||
name='due_at',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='servicepayment',
|
||||
name='service',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Charge',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('due_date', models.DateField()),
|
||||
('is_paid', models.BooleanField(default=False)),
|
||||
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='billing.service')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='servicepayment',
|
||||
name='charge',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='billing.charge'),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-23 20:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0009_remove_service_due_at_remove_servicepayment_service_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='charge',
|
||||
name='amount',
|
||||
field=models.DecimalField(decimal_places=3, max_digits=5, null=True),
|
||||
),
|
||||
]
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-24 19:22
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0010_charge_amount'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Invoice',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('invoice_number', models.CharField(max_length=100)),
|
||||
('date_created', models.DateField(verbose_name=django.db.models.fields.DateField)),
|
||||
('charge', models.ManyToManyField(to='billing.charge')),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,31 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-24 19:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0011_invoice'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='servicepayment',
|
||||
name='charge',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Receipt',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('receipt_number', models.CharField(max_length=100)),
|
||||
('date_generated', models.DateField()),
|
||||
('payment', models.ManyToManyField(to='billing.servicepayment')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='servicepayment',
|
||||
name='charge',
|
||||
field=models.ManyToManyField(null=True, to='billing.charge'),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-24 19:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0012_remove_servicepayment_charge_receipt_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='date_created',
|
||||
field=models.DateField(),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.5 on 2024-02-24 19:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0013_alter_invoice_date_created'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='servicepayment',
|
||||
name='comment',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
]
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,66 @@
|
||||
from django.db import models
|
||||
from osinacore.models import *
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class RecurringCycle(models.Model):
|
||||
months = models.IntegerField()
|
||||
def __str__(self):
|
||||
return f"{self.months} months"
|
||||
|
||||
|
||||
class Service(models.Model):
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
def __str__(self):
|
||||
return f"{self.service}'s charge due {self.due_date}"
|
||||
|
||||
|
||||
class PaymentType(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class ServicePayment(models.Model):
|
||||
charge = models.ManyToManyField(Charge, 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
|
||||
|
||||
|
||||
class Receipt(models.Model):
|
||||
receipt_number = models.CharField(max_length=100)
|
||||
payment = models.ManyToManyField(ServicePayment)
|
||||
date_generated = models.DateField()
|
||||
def __str__(self):
|
||||
return self.receipt_number
|
@ -0,0 +1,25 @@
|
||||
# tasks.py
|
||||
from celery import shared_task
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from .models import *
|
||||
|
||||
@shared_task
|
||||
def create_charges_for_recurring_services():
|
||||
today = timezone.now().date()
|
||||
active_recurring_services = Service.objects.filter(active=True, recurring=True)
|
||||
|
||||
for service in active_recurring_services:
|
||||
# Get the last charge for the service
|
||||
last_charge = service.charge_set.order_by('-due_date').first()
|
||||
|
||||
if last_charge and last_charge.due_date <= today:
|
||||
# Calculate the new due date for the charge based on the recurring cycle
|
||||
new_due_date = today + timedelta(days=service.recurring_cycle.months * 30)
|
||||
|
||||
# Create a new charge for the service
|
||||
Charge.objects.create(
|
||||
service=service,
|
||||
amount=service.amount,
|
||||
due_date=new_due_date
|
||||
)
|
@ -0,0 +1,26 @@
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from .models import *
|
||||
# Create your tests here.
|
||||
|
||||
|
||||
@shared_task
|
||||
def create_charges_for_recurring_services():
|
||||
today = timezone.now().date()
|
||||
active_recurring_services = Service.objects.filter(active=True, recurring=True)
|
||||
|
||||
for service in active_recurring_services:
|
||||
# Get the last charge for the service
|
||||
last_charge = service.charge_set.order_by('-due_date').first()
|
||||
|
||||
if last_charge and last_charge.due_date <= today:
|
||||
# Calculate the new due date for the charge based on the recurring cycle
|
||||
new_due_date = today + timedelta(days=service.recurring_cycle.number_of_months * 30)
|
||||
|
||||
# Create a new charge for the service
|
||||
Charge.objects.create(
|
||||
service=service,
|
||||
amount=service.amount,
|
||||
due_date=new_due_date
|
||||
)
|
@ -0,0 +1,13 @@
|
||||
from django.contrib import admin
|
||||
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
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path('check', views.create_charges_for_recurring_services, name='check'),
|
||||
|
||||
]
|
@ -0,0 +1,29 @@
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from .models import *
|
||||
from django.http import JsonResponse
|
||||
|
||||
# Create your views here.
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
def create_charges_for_recurring_services(request):
|
||||
today = timezone.now().date()
|
||||
active_recurring_services = Service.objects.filter(active=True, recurring=True)
|
||||
|
||||
for service in active_recurring_services:
|
||||
# Get the last charge for the service
|
||||
last_charge = service.charge_set.order_by('-due_date').first()
|
||||
|
||||
if last_charge and last_charge.due_date <= today:
|
||||
# Calculate the new due date for the charge based on the recurring cycle
|
||||
new_due_date = today + timedelta(days=service.recurring_cycle.number_of_months * 30)
|
||||
|
||||
# Create a new charge for the service
|
||||
Charge.objects.create(
|
||||
service=service,
|
||||
amount=service.amount,
|
||||
due_date=new_due_date
|
||||
)
|
||||
return JsonResponse({'status': 'success'})
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,161 @@
|
||||
"""osinaweb URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/4.1/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
|
||||
from django.urls import path
|
||||
from osinacore import views
|
||||
from django.conf.urls.static import static
|
||||
from django.conf import settings
|
||||
|
||||
urlpatterns = [
|
||||
path('login', views.signin, name='signin'),
|
||||
path('logout/', views.signout, name='signout'),
|
||||
path('', views.home, name='home'),
|
||||
path('my-projects/', views.my_projects, name='my-projects'),
|
||||
path('my-tasks/', views.my_tasks, name='my-tasks'),
|
||||
path('my-notes/', views.my_notes, name='my-notes'),
|
||||
path('customers/', views.customers, name='customers'),
|
||||
path('addcustomer/', views.add_customer, name='addcustomer'),
|
||||
path('add-business/', views.addbusiness, name='addbusiness'),
|
||||
path('businesses/<str:business_id>/', views.businessdetails, name='businessdetails'),
|
||||
path('businesses/', views.businesses, name='businesses'),
|
||||
path('addstaff/', views.addstaff, name='adduser'),
|
||||
path('staffs/', views.staffs, name='users'),
|
||||
path('staffpositions/', views.staff_positions, name='staffpositions'),
|
||||
path('createproject/', views.create_project, name='createproject'),
|
||||
path('createepic/<str:project_id>/', views.create_epic, name='createepic'),
|
||||
path('createtask/', views.create_task, name='createtask'),
|
||||
path('createtask/<str:project_id>/', views.createtask_project, name='createtaskproject'),
|
||||
path('createuserstorytask/<int:requirement_id>/', views.create_user_story_task, name='createuserstorytask'),
|
||||
path('createtaskepic/', views.createtask_epic, name='createtaskepic'),
|
||||
path('projecttypes/', views.project_types, name='projecttypes'),
|
||||
path('businesstypes/', views.business_types, name='businesstypes'),
|
||||
path('references/', views.references, name='references'),
|
||||
path('tags/', views.tags, name='tags'),
|
||||
path('dailyreports/', views.daily_reports, name='dailyreports'),
|
||||
path('add-dailyreport/', views.add_daily_report, name='adddailyreport'),
|
||||
|
||||
|
||||
|
||||
|
||||
#Detail pages urls
|
||||
path('businesses/<str:business_id>/', views.businessdetails, name='businessdetails'),
|
||||
path('customers/<str:customer_id>/', views.customerdetails, name='customerdetails'),
|
||||
path('staffs/<str:staff_id>/', views.staffdetails, name='userdetails'),
|
||||
path('tasks/<str:task_id>/', views.detailed_task, name='detailed-task'),
|
||||
path('projectdetails/<str:project_id>/', views.detailed_project, name='detailed-project'),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#Fetch urls
|
||||
path('get_tasks/<int:epic_id>/', views.get_tasks, name='get_tasks'),
|
||||
path('open_tasks_for_project/<int:project_id>/', views.open_tasks_for_project, name='open_tasks_for_project'),
|
||||
|
||||
|
||||
#Modals urls
|
||||
path('add-status/', views.add_status_modal, name='addstatus'),
|
||||
path('add-note/', views.add_note_modal, name='addnote'),
|
||||
path('add-project-note/<str:project_id>/', views.add_project_note_modal, name='addprojectnote'),
|
||||
path('add-file/', views.add_file_modal, name='addfile'),
|
||||
path('add-user-story/<str:project_id>/', views.add_user_story_modal, name='adduserstory'),
|
||||
path('add-credentials/', views.add_credentials_modal, name='addcredentials'),
|
||||
path('update-status/<str:task_id>/', views.update_status_modal, name='updatestatus'),
|
||||
path('add-point/<str:task_id>/', views.add_point_modal, name='addpoint'),
|
||||
path('show-points/<str:task_id>/', views.show_points_modal, name='showpoints'),
|
||||
path('add-time/', views.add_time_modal, name='addtime'),
|
||||
path('timeline/', views.timeline_modal, name='timeline'),
|
||||
path('add-projecttype/', views.add_projecttype_modal, name='addprojecttype'),
|
||||
path('add-businesstype/', views.add_businesstype_modal, name='addbusinesstype'),
|
||||
path('add-reference/', views.add_reference_modal, name='addreference'),
|
||||
path('add-tag/', views.add_tag_modal, name='addtag'),
|
||||
path('add-businesscustomer/', views.add_business_modal, name='addbusinesscustomer'),
|
||||
path('add-staffposition/', views.staff_position_modal, name='addstaffposition'),
|
||||
path('statusmobilemodal/', views.status_mobile_modal, name='statusmobilemodal'),
|
||||
path('userrecentativities/<int:user_id>', views.user_recent_activities_modal, name='userrecentativities'),
|
||||
|
||||
|
||||
#Delete Modals
|
||||
path('deletecustomermodal/<int:customer_id>', views.delete_customer_modal, name='deletecustomermodal'),
|
||||
path('deletebusinessmodal/<int:business_id>', views.delete_business_modal, name='deletebusinessmodal'),
|
||||
path('deletestaffmodal/<int:staff_id>', views.delete_staff_modal, name='deletestaffmodal'),
|
||||
path('deleteprojectmodal/<int:project_id>', views.delete_project_modal, name='deleteprojectmodal'),
|
||||
path('deletetaskmodal/<int:task_id>', views.delete_task_modal, name='deletetaskmodal'),
|
||||
path('deletenotemodal/<int:note_id>', views.delete_note_modal, name='deletenotemodal'),
|
||||
path('deleteprojectnotemodal/<int:note_id>/', views.delete_project_note_modal, name='deleteprojectnotemodal'),
|
||||
|
||||
path('deletepointmodal/<int:point_id>/<str:task_id>/', views.delete_point_modal, name='deletepointmodal'),
|
||||
path('deletetaskpointmodal/<int:point_id>/<str:task_id>/', views.delete_task_point_modal, name='deletetaskpointmodal'),
|
||||
|
||||
|
||||
#Save Urls
|
||||
path('save_note/', views.save_note, name='save_note'),
|
||||
path('save_project_note/', views.save_project_note, name='save_project_note'),
|
||||
path('save_project/', views.save_project, name='save_project'),
|
||||
path('save_epic/', views.save_epic, name='save_epic'),
|
||||
path('save_task/', views.save_task, name='save_task'),
|
||||
path('save_business/', views.save_business, name='save_business'),
|
||||
path('save_business_modal/', views.save_business_modal, name='save_business_modal'),
|
||||
path('save_customer/', views.save_customer, name='save_customer'),
|
||||
path('save_staff/', views.save_staff, name='save_staff'),
|
||||
path('save_status/', views.save_status, name='save_status'),
|
||||
path('save_projecttype/', views.save_projecttype, name='save_projecttype'),
|
||||
path('save_businesstype/', views.save_businesstype, name='save_businesstype'),
|
||||
path('save_reference/', views.save_reference, name='save_reference'),
|
||||
path('save_tag/', views.save_tag, name='save_tag'),
|
||||
path('save_point/', views.save_point, name='save_point'),
|
||||
path('save_staffposition/', views.save_staffposition, name='save_staffposition'),
|
||||
path('save_dailyreport/', views.save_dailyreport, name='save_dailyreport'),
|
||||
|
||||
|
||||
#Edit Pages
|
||||
path('edit-project/<str:project_id>/', views.edit_project, name='editproject'),
|
||||
path('edit-epic/', views.edit_epic, name='editepic'),
|
||||
path('edit-task/<str:task_id>', views.edit_task, name='edittask'),
|
||||
path('edit-customer/<str:customer_id>/', views.edit_customer, name='editcustomer'),
|
||||
path('edit-business/<str:business_id>/', views.edit_business, name='editbusiness'),
|
||||
path('edit-staff/<str:staff_id>/', views.edit_staff, name='editstaff'),
|
||||
path('edit-projecttype/<int:projecttype_id>', views.edit_project_type, name='editprojecttype'),
|
||||
path('edit-businesstype/<int:businesstype_id>', views.edit_business_type, name='editbusinesstype'),
|
||||
path('edit-reference/<int:reference_id>', views.edit_reference, name='editreference'),
|
||||
path('edit-tag/<int:tag_id>', views.edit_tag, name='edittag'),
|
||||
path('edits-taffposition/', views.edit_staff_position, name='editstaffposition'),
|
||||
path('mark_point_working_on/<int:point_id>/<str:task_id>/', views.mark_point_working_on, name='mark_point_working_on'),
|
||||
path('mark_point_working_on_task_page/<int:point_id>/<str:task_id>/', views.mark_point_working_on_task_page, name='mark_point_working_on_task_page'),
|
||||
path('mark_point_completed/<int:point_id>/<str:task_id>/', views.mark_point_completed, name='mark_point_completed'),
|
||||
path('mark_point_completed_task_page/<int:point_id>/<str:task_id>/', views.mark_point_completed_task_page, name='mark_point_completed_task_page'),
|
||||
path('mark_point_paused/<int:point_id>/<str:task_id>/', views.mark_point_paused, name='mark_point_paused'),
|
||||
|
||||
|
||||
|
||||
#Fetch Urls
|
||||
path('getupdatedlaststatus/', views.get_updated_last_status, name='getupdatedlaststatus'),
|
||||
path('getupdatedactivities/', views.get_latest_activities, name='getupdatedactivities'),
|
||||
|
||||
# TO FETCH THE EPICS OF THE SELECTED PROJECT WHEN EDITING A TASK
|
||||
path('fetch_epics/', views.fetch_epics, name='fetch_epics'),
|
||||
|
||||
|
||||
path('get_point_total_time/<int:point_id>/', views.get_point_total_time, name='get_point_total_time'),
|
||||
|
||||
|
||||
|
||||
#CUSTOMER DASHBOARD
|
||||
path('customerdashboard/', views.customer_index, name='customerdashboard'),
|
||||
path('customerinvoices/', views.customer_invoices, name='customerinvoices'),
|
||||
]
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue