Add first accounting implementation

This commit is contained in:
Skia 2016-01-28 16:53:37 +01:00
parent 2b999d87ba
commit f71ce2f7df
12 changed files with 245 additions and 4 deletions

0
accounting/__init__.py Normal file
View File

12
accounting/admin.py Normal file
View File

@ -0,0 +1,12 @@
from django.contrib import admin
from accounting.models import *
admin.site.register(Customer)
admin.site.register(ProductType)
admin.site.register(Product)
admin.site.register(GeneralJournal)
admin.site.register(GenericInvoice)

View File

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
import accounting.models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_auto_20160128_0842'),
]
operations = [
migrations.CreateModel(
name='Customer',
fields=[
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, primary_key=True, serialize=False)),
('account_id', models.CharField(unique=True, verbose_name='account id', max_length=10)),
],
options={
'verbose_name': 'customer',
'verbose_name_plural': 'customers',
},
),
migrations.CreateModel(
name='GeneralJournal',
fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
('start_date', models.DateField(verbose_name='start date')),
('end_date', models.DateField(null=True, verbose_name='end date', blank=True, default=None)),
('name', models.CharField(verbose_name='name', max_length=30)),
('closed', models.BooleanField(default=False, verbose_name='is closed')),
],
),
migrations.CreateModel(
name='GenericInvoice',
fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
('name', models.CharField(verbose_name='name', max_length=100)),
('journal', models.ForeignKey(to='accounting.GeneralJournal', related_name='invoices')),
],
),
migrations.CreateModel(
name='Product',
fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
('name', models.CharField(verbose_name='name', max_length=30)),
('description', models.TextField(verbose_name='description')),
('code', models.CharField(verbose_name='code', max_length=10)),
('purchase_price', accounting.models.CurrencyField(max_digits=12, decimal_places=2, verbose_name='purchase price')),
('selling_price', accounting.models.CurrencyField(max_digits=12, decimal_places=2, verbose_name='selling price')),
('special_selling_price', accounting.models.CurrencyField(max_digits=12, decimal_places=2, verbose_name='special selling price')),
('icon', models.ImageField(null=True, upload_to='products')),
],
),
migrations.CreateModel(
name='ProductType',
fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
('name', models.CharField(verbose_name='name', max_length=30)),
('description', models.TextField(verbose_name='description')),
('icon', models.ImageField(null=True, upload_to='products')),
],
),
migrations.AddField(
model_name='product',
name='product_type',
field=models.ForeignKey(null=True, to='accounting.ProductType', related_name='products'),
),
]

View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='product',
name='description',
field=models.TextField(verbose_name='description', blank=True),
),
migrations.AlterField(
model_name='product',
name='icon',
field=models.ImageField(null=True, upload_to='products', blank=True),
),
migrations.AlterField(
model_name='product',
name='product_type',
field=models.ForeignKey(to='accounting.ProductType', null=True, blank=True, related_name='products'),
),
migrations.AlterField(
model_name='producttype',
name='description',
field=models.TextField(verbose_name='description', null=True, blank=True),
),
migrations.AlterField(
model_name='producttype',
name='icon',
field=models.ImageField(null=True, upload_to='products', blank=True),
),
]

View File

94
accounting/models.py Normal file
View File

@ -0,0 +1,94 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from decimal import Decimal
from core.models import User
class CurrencyField(models.DecimalField):
"""
This is a custom database field used for currency
"""
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
kwargs['max_digits'] = 12
kwargs['decimal_places'] = 2
super(CurrencyField, self).__init__(*args, **kwargs)
def to_python(self, value):
try:
return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
except AttributeError:
return None
class Customer(models.Model):
"""
This class extends a user to make a customer. It adds some basic customers informations, such as the accound ID, and
is used by other accounting classes as reference to the customer, rather than using User
"""
user = models.OneToOneField(User, primary_key=True)
account_id = models.CharField(_('account id'), max_length=10, unique=True)
class Meta:
verbose_name = _('customer')
verbose_name_plural = _('customers')
def __str__(self):
return self.user.username
class ProductType(models.Model):
"""
This describes a product type
Useful only for categorizing, changes are made at the product level
"""
name = models.CharField(_('name'), max_length=30)
description = models.TextField(_('description'), null=True, blank=True)
icon = models.ImageField(upload_to='products', null=True, blank=True)
def __str__(self):
return self.name
class Product(models.Model):
"""
This describes a product, with all its related informations
"""
name = models.CharField(_('name'), max_length=30)
description = models.TextField(_('description'), blank=True)
product_type = models.ForeignKey(ProductType, related_name='products', null=True, blank=True)
code = models.CharField(_('code'), max_length=10)
purchase_price = CurrencyField(_('purchase price'))
selling_price = CurrencyField(_('selling price'))
special_selling_price = CurrencyField(_('special selling price'))
icon = models.ImageField(upload_to='products', null=True, blank=True)
def __str__(self):
return self.name
class GeneralJournal(models.Model):
"""
Class storing all the invoices for a period of time
"""
start_date = models.DateField(_('start date'))
end_date = models.DateField(_('end date'), null=True, blank=True, default=None)
name = models.CharField(_('name'), max_length=30)
closed = models.BooleanField(_('is closed'), default=False)
# When clubs are done: ForeignKey(Proprietary)
def __str__(self):
return self.name
class GenericInvoice(models.Model):
"""
This class is a generic invoice, made to be extended with some special cases (eg: for the internal accounting, payment
system, etc...)
"""
journal = models.ForeignKey(GeneralJournal, related_name="invoices", null=False)
name = models.CharField(_('name'), max_length=100)
def __str__(self):
return self.journal.name+' - '+self.name
# TODO: CountingInvoice in Counting app extending GenericInvoice
# - ManyToMany Product

3
accounting/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
accounting/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import User, Page, Group from core.models import User, Page, Group
admin.site.register(User) admin.site.register(User)

View File

@ -2,7 +2,10 @@ import os
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.core.management import call_command from django.core.management import call_command
from django.conf import settings from django.conf import settings
from core.models import Group, User, Page, PageRev from core.models import Group, User, Page, PageRev
from accounting.models import Customer, GeneralJournal, ProductType, Product
class Command(BaseCommand): class Command(BaseCommand):
help = "Set up a new instance of the Sith AE" help = "Set up a new instance of the Sith AE"
@ -13,8 +16,9 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
try: try:
os.unlink(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), 'db.sqlite3')) os.unlink(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), 'db.sqlite3'))
except: os.mkdir(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))+'/data')
pass except Exception as e:
print(e)
call_command('migrate') call_command('migrate')
u = User(username='root', last_name="", first_name="Bibou", u = User(username='root', last_name="", first_name="Bibou",
email="ae.info@utbm.fr", email="ae.info@utbm.fr",
@ -45,3 +49,11 @@ class Command(BaseCommand):
Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site. Cette page vise à documenter la syntaxe *Markdown* utilisée sur le site.
""").save() """).save()
# Accounting test values:
Customer(user=s, account_id="6568j").save()
p = ProductType(name="Bières bouteilles")
p.save()
Product(name="Barbar", code="BARB", product_type=p, purchase_price="1.50", selling_price="1.7",
special_selling_price="1.6").save()
GeneralJournal(start_date="2015-06-12", name="A15").save()

View File

@ -39,6 +39,7 @@ INSTALLED_APPS = (
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'core', 'core',
'ae', 'ae',
'accounting',
) )
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
@ -98,6 +99,9 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
# Medias
MEDIA_ROOT = './data/'
MEDIA_URL = '/data/'
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/ # https://docs.djangoproject.com/en/1.8/howto/static-files/

View File

@ -15,6 +15,8 @@ Including another URLconf
""" """
from django.conf.urls import include, url from django.conf.urls import include, url
from django.contrib import admin from django.contrib import admin
from django.conf.urls.static import static
from django.conf import settings
handler403 = "core.views.forbidden" handler403 = "core.views.forbidden"
handler404 = "core.views.not_found" handler404 = "core.views.not_found"
@ -23,4 +25,4 @@ urlpatterns = [
url(r'^', include('core.urls', namespace="core", app_name="core")), url(r'^', include('core.urls', namespace="core", app_name="core")),
url(r'^ae/', include('ae.urls', namespace="ae", app_name="ae")), url(r'^ae/', include('ae.urls', namespace="ae", app_name="ae")),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
] ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # TODO: remove me for production!!!