🚨 format python code using black
This commit is contained in:
1 parent
9c4a5b3d84
commit
a052397990
24 files changed
+628
-392
No files matched your search
@@ -15,6 +15,7 @@ Django = "==3.2.*"
|
|||||||
Pillow = "==8.*"
|
Pillow = "==8.*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
black = "==21.9b0"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.9"
|
python_version = "3.9"
|
||||||
Generated
+98
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "c079e1ab20a4d9bd30eed7407120599adbc7ef1de635764c5adcab332a08ca1e"
|
"sha256": "91cd00454ddb91ac311b12f438935d7f4eb3e79e3650bb7a522e8fe3b00e78a2"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -194,5 +194,101 @@
|
|||||||
"version": "==1.26.7"
|
"version": "==1.26.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {}
|
"develop": {
|
||||||
|
"black": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:380f1b5da05e5a1429225676655dddb96f5ae8c75bdf91e53d798871b902a115",
|
||||||
|
"sha256:7de4cfc7eb6b710de325712d40125689101d21d25283eed7e9998722cf10eb91"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==21.9b0"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3",
|
||||||
|
"sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==8.0.3"
|
||||||
|
},
|
||||||
|
"mypy-extensions": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||||
|
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||||
|
],
|
||||||
|
"version": "==0.4.3"
|
||||||
|
},
|
||||||
|
"pathspec": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a",
|
||||||
|
"sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"
|
||||||
|
],
|
||||||
|
"version": "==0.9.0"
|
||||||
|
},
|
||||||
|
"platformdirs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2",
|
||||||
|
"sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==2.4.0"
|
||||||
|
},
|
||||||
|
"regex": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0c186691a7995ef1db61205e00545bf161fb7b59cdb8c1201c89b333141c438a",
|
||||||
|
"sha256:0dcc0e71118be8c69252c207630faf13ca5e1b8583d57012aae191e7d6d28b84",
|
||||||
|
"sha256:0f7552429dd39f70057ac5d0e897e5bfe211629652399a21671e53f2a9693a4e",
|
||||||
|
"sha256:129472cd06062fb13e7b4670a102951a3e655e9b91634432cfbdb7810af9d710",
|
||||||
|
"sha256:13ec99df95003f56edcd307db44f06fbeb708c4ccdcf940478067dd62353181e",
|
||||||
|
"sha256:1f2b59c28afc53973d22e7bc18428721ee8ca6079becf1b36571c42627321c65",
|
||||||
|
"sha256:2b20f544cbbeffe171911f6ce90388ad36fe3fad26b7c7a35d4762817e9ea69c",
|
||||||
|
"sha256:2fb698037c35109d3c2e30f2beb499e5ebae6e4bb8ff2e60c50b9a805a716f79",
|
||||||
|
"sha256:34d870f9f27f2161709054d73646fc9aca49480617a65533fc2b4611c518e455",
|
||||||
|
"sha256:391703a2abf8013d95bae39145d26b4e21531ab82e22f26cd3a181ee2644c234",
|
||||||
|
"sha256:450dc27483548214314640c89a0f275dbc557968ed088da40bde7ef8fb52829e",
|
||||||
|
"sha256:45b65d6a275a478ac2cbd7fdbf7cc93c1982d613de4574b56fd6972ceadb8395",
|
||||||
|
"sha256:5095a411c8479e715784a0c9236568ae72509450ee2226b649083730f3fadfc6",
|
||||||
|
"sha256:530fc2bbb3dc1ebb17f70f7b234f90a1dd43b1b489ea38cea7be95fb21cdb5c7",
|
||||||
|
"sha256:56f0c81c44638dfd0e2367df1a331b4ddf2e771366c4b9c5d9a473de75e3e1c7",
|
||||||
|
"sha256:5e9c9e0ce92f27cef79e28e877c6b6988c48b16942258f3bc55d39b5f911df4f",
|
||||||
|
"sha256:6d7722136c6ed75caf84e1788df36397efdc5dbadab95e59c2bba82d4d808a4c",
|
||||||
|
"sha256:74d071dbe4b53c602edd87a7476ab23015a991374ddb228d941929ad7c8c922e",
|
||||||
|
"sha256:7b568809dca44cb75c8ebb260844ea98252c8c88396f9d203f5094e50a70355f",
|
||||||
|
"sha256:80bb5d2e92b2258188e7dcae5b188c7bf868eafdf800ea6edd0fbfc029984a88",
|
||||||
|
"sha256:8d1cdcda6bd16268316d5db1038965acf948f2a6f43acc2e0b1641ceab443623",
|
||||||
|
"sha256:9f665677e46c5a4d288ece12fdedf4f4204a422bb28ff05f0e6b08b7447796d1",
|
||||||
|
"sha256:a30513828180264294953cecd942202dfda64e85195ae36c265daf4052af0464",
|
||||||
|
"sha256:a7a986c45d1099a5de766a15de7bee3840b1e0e1a344430926af08e5297cf666",
|
||||||
|
"sha256:a940ca7e7189d23da2bfbb38973832813eab6bd83f3bf89a977668c2f813deae",
|
||||||
|
"sha256:ab7c5684ff3538b67df3f93d66bd3369b749087871ae3786e70ef39e601345b0",
|
||||||
|
"sha256:be04739a27be55631069b348dda0c81d8ea9822b5da10b8019b789e42d1fe452",
|
||||||
|
"sha256:c0938ddd60cc04e8f1faf7a14a166ac939aac703745bfcd8e8f20322a7373019",
|
||||||
|
"sha256:cb46b542133999580ffb691baf67410306833ee1e4f58ed06b6a7aaf4e046952",
|
||||||
|
"sha256:d134757a37d8640f3c0abb41f5e68b7cf66c644f54ef1cb0573b7ea1c63e1509",
|
||||||
|
"sha256:de557502c3bec8e634246588a94e82f1ee1b9dfcfdc453267c4fb652ff531570",
|
||||||
|
"sha256:ded0c4a3eee56b57fcb2315e40812b173cafe79d2f992d50015f4387445737fa",
|
||||||
|
"sha256:e1dae12321b31059a1a72aaa0e6ba30156fe7e633355e445451e4021b8e122b6",
|
||||||
|
"sha256:eb672217f7bd640411cfc69756ce721d00ae600814708d35c930930f18e8029f",
|
||||||
|
"sha256:ee684f139c91e69fe09b8e83d18b4d63bf87d9440c1eb2eeb52ee851883b1b29",
|
||||||
|
"sha256:f3f9a91d3cc5e5b0ddf1043c0ae5fa4852f18a1c0050318baf5fc7930ecc1f9c"
|
||||||
|
],
|
||||||
|
"version": "==2021.10.23"
|
||||||
|
},
|
||||||
|
"tomli": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee",
|
||||||
|
"sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==1.2.2"
|
||||||
|
},
|
||||||
|
"typing-extensions": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e",
|
||||||
|
"sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7",
|
||||||
|
"sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
|
||||||
|
],
|
||||||
|
"version": "==3.10.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+82
-86
@@ -1,5 +1,6 @@
|
|||||||
# Django settings for bbs project.
|
# Django settings for bbs project.
|
||||||
import os
|
import os
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
@@ -10,92 +11,89 @@ ADMINS = (
|
|||||||
MANAGERS = ADMINS
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
'NAME': 'db.sqlite3',
|
"NAME": "db.sqlite3",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
TIME_ZONE = 'Europe/Oslo'
|
TIME_ZONE = "Europe/Oslo"
|
||||||
LANGUAGE_CODE = 'nb'
|
LANGUAGE_CODE = "nb"
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
# Media and uploads
|
# Media and uploads
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = "/media/"
|
||||||
|
|
||||||
# Static files
|
# Static files
|
||||||
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
|
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = "/static/"
|
||||||
STATICFILES_DIRS = (
|
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
|
||||||
os.path.join(BASE_DIR, 'static'),
|
|
||||||
)
|
|
||||||
|
|
||||||
STATICFILES_FINDERS = (
|
STATICFILES_FINDERS = (
|
||||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Make this unique, and don't share it with anybody.
|
# Make this unique, and don't share it with anybody.
|
||||||
SECRET_KEY = 'osv$uy-ea$*%lc)+xiwt5@2b7au%u*c!pa6hmh#vl)*+tp%fss'
|
SECRET_KEY = "osv$uy-ea$*%lc)+xiwt5@2b7au%u*c!pa6hmh#vl)*+tp%fss"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'DIRS': [
|
"DIRS": [
|
||||||
os.path.join(BASE_DIR, 'templates'),
|
os.path.join(BASE_DIR, "templates"),
|
||||||
],
|
],
|
||||||
'APP_DIRS': True,
|
"APP_DIRS": True,
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'context_processors': [
|
"context_processors": [
|
||||||
'django.contrib.auth.context_processors.auth',
|
"django.contrib.auth.context_processors.auth",
|
||||||
'django.template.context_processors.request',
|
"django.template.context_processors.request",
|
||||||
'django.template.context_processors.debug',
|
"django.template.context_processors.debug",
|
||||||
'django.template.context_processors.i18n',
|
"django.template.context_processors.i18n",
|
||||||
'django.template.context_processors.media',
|
"django.template.context_processors.media",
|
||||||
'django.template.context_processors.static',
|
"django.template.context_processors.static",
|
||||||
'django.template.context_processors.tz',
|
"django.template.context_processors.tz",
|
||||||
'django.contrib.messages.context_processors.messages',
|
"django.contrib.messages.context_processors.messages",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.common.CommonMiddleware',
|
"django.middleware.common.CommonMiddleware",
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'bbssite.urls'
|
ROOT_URLCONF = "bbssite.urls"
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.auth',
|
"django.contrib.auth",
|
||||||
'django.contrib.contenttypes',
|
"django.contrib.contenttypes",
|
||||||
'django.contrib.sessions',
|
"django.contrib.sessions",
|
||||||
'django.contrib.sites',
|
"django.contrib.sites",
|
||||||
'django.contrib.messages',
|
"django.contrib.messages",
|
||||||
'django.contrib.staticfiles',
|
"django.contrib.staticfiles",
|
||||||
'django.contrib.admin',
|
"django.contrib.admin",
|
||||||
|
"widget_tweaks",
|
||||||
'widget_tweaks',
|
"bootstrapform",
|
||||||
'bootstrapform',
|
"django_extensions",
|
||||||
'django_extensions',
|
"rest_framework",
|
||||||
'rest_framework',
|
"easy_thumbnails",
|
||||||
'easy_thumbnails',
|
"raven.contrib.django.raven_compat",
|
||||||
'raven.contrib.django.raven_compat',
|
|
||||||
]
|
]
|
||||||
LOCAL_APPS = [
|
LOCAL_APPS = [
|
||||||
'main',
|
"main",
|
||||||
]
|
]
|
||||||
INSTALLED_APPS += LOCAL_APPS
|
INSTALLED_APPS += LOCAL_APPS
|
||||||
|
|
||||||
LOGIN_REDIRECT_URL = '/'
|
LOGIN_REDIRECT_URL = "/"
|
||||||
|
|
||||||
SESSION_COOKIE_AGE = 1209600 # 15 minutes
|
SESSION_COOKIE_AGE = 1209600 # 15 minutes
|
||||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||||
@@ -105,54 +103,52 @@ BBS_LIMIT_DEPOSITS = False
|
|||||||
|
|
||||||
# Thumbnails
|
# Thumbnails
|
||||||
THUMBNAIL_ALIASES = {
|
THUMBNAIL_ALIASES = {
|
||||||
'': {
|
"": {
|
||||||
'product': {'size': (64, 64), 'crop': True},
|
"product": {"size": (64, 64), "crop": True},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
'version': 1,
|
"version": 1,
|
||||||
'disable_existing_loggers': False,
|
"disable_existing_loggers": False,
|
||||||
'root': {
|
"root": {
|
||||||
'level': 'INFO',
|
"level": "INFO",
|
||||||
'handlers': ['console', 'sentry'],
|
"handlers": ["console", "sentry"],
|
||||||
},
|
},
|
||||||
'formatters': {
|
"formatters": {
|
||||||
'verbose': {
|
"verbose": {"format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"},
|
||||||
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
|
},
|
||||||
|
"handlers": {
|
||||||
|
"sentry": {
|
||||||
|
"level": "WARNING",
|
||||||
|
"class": "raven.contrib.django.raven_compat.handlers.SentryHandler",
|
||||||
|
},
|
||||||
|
"console": {
|
||||||
|
"level": "DEBUG",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "verbose",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'handlers': {
|
"loggers": {
|
||||||
'sentry': {
|
"django.db.backends": {
|
||||||
'level': 'WARNING',
|
"level": "ERROR",
|
||||||
'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler',
|
"handlers": ["console"],
|
||||||
|
"propagate": False,
|
||||||
},
|
},
|
||||||
'console': {
|
"raven": {
|
||||||
'level': 'DEBUG',
|
"level": "WARNING",
|
||||||
'class': 'logging.StreamHandler',
|
"handlers": ["console"],
|
||||||
'formatter': 'verbose'
|
"propagate": False,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'loggers': {
|
"sentry.errors": {
|
||||||
'django.db.backends': {
|
"level": "WARNING",
|
||||||
'level': 'ERROR',
|
"handlers": ["console"],
|
||||||
'handlers': ['console'],
|
"propagate": False,
|
||||||
'propagate': False,
|
|
||||||
},
|
|
||||||
'raven': {
|
|
||||||
'level': 'WARNING',
|
|
||||||
'handlers': ['console'],
|
|
||||||
'propagate': False,
|
|
||||||
},
|
|
||||||
'sentry.errors': {
|
|
||||||
'level': 'WARNING',
|
|
||||||
'handlers': ['console'],
|
|
||||||
'propagate': False,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .local_settings import *
|
from .local_settings import *
|
||||||
|
|||||||
+15
-8
@@ -11,17 +11,24 @@ from main import urls as main_urls
|
|||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
re_path(r'', include(main_urls)),
|
re_path(r"", include(main_urls)),
|
||||||
path('accounts/login/', LoginView.as_view(), name='django.contrib.auth.views.login'),
|
path("accounts/login/", LoginView.as_view(), name="django.contrib.auth.views.login"),
|
||||||
path('accounts/logout/', LogoutView.as_view(), name='django.contrib.auth.views.logout'),
|
path(
|
||||||
|
"accounts/logout/",
|
||||||
path('admin/', admin.site.urls),
|
LogoutView.as_view(),
|
||||||
|
name="django.contrib.auth.views.logout",
|
||||||
|
),
|
||||||
|
path("admin/", admin.site.urls),
|
||||||
]
|
]
|
||||||
urlpatterns += staticfiles_urlpatterns()
|
urlpatterns += staticfiles_urlpatterns()
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
re_path(r'^media/(?P<path>.*)$', serve, {
|
re_path(
|
||||||
'document_root': settings.MEDIA_ROOT,
|
r"^media/(?P<path>.*)$",
|
||||||
})
|
serve,
|
||||||
|
{
|
||||||
|
"document_root": settings.MEDIA_ROOT,
|
||||||
|
},
|
||||||
|
)
|
||||||
]
|
]
|
||||||
+1
-1
@@ -2,7 +2,7 @@ import os
|
|||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
from raven.contrib.django.middleware.wsgi import Sentry
|
from raven.contrib.django.middleware.wsgi import Sentry
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bbssite.settings')
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bbssite.settings")
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
application = Sentry(application)
|
application = Sentry(application)
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
black main bbssite
|
||||||
@@ -5,6 +5,7 @@ from main.models import *
|
|||||||
class ProductAdmin(admin.ModelAdmin):
|
class ProductAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Product, ProductAdmin)
|
admin.site.register(Product, ProductAdmin)
|
||||||
admin.site.register(InventoryTransaction)
|
admin.site.register(InventoryTransaction)
|
||||||
admin.site.register(Order)
|
admin.site.register(Order)
|
||||||
|
|||||||
+18
-9
@@ -8,33 +8,42 @@ from main.models import Product, OrderLine, Order
|
|||||||
class ProductSerializer(ModelSerializer):
|
class ProductSerializer(ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Product
|
model = Product
|
||||||
fields = '__all__'
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
class ProductStatSerializer(ModelSerializer):
|
class ProductStatSerializer(ModelSerializer):
|
||||||
EXTERNAL_USER = 'Ekstern'
|
EXTERNAL_USER = "Ekstern"
|
||||||
counts = serializers.SerializerMethodField()
|
counts = serializers.SerializerMethodField()
|
||||||
|
|
||||||
def _format_counts(self, per_user):
|
def _format_counts(self, per_user):
|
||||||
per_user = list(per_user)
|
per_user = list(per_user)
|
||||||
for row in per_user:
|
for row in per_user:
|
||||||
first = row.pop('order__customer__first_name')
|
first = row.pop("order__customer__first_name")
|
||||||
last = row.pop('order__customer__last_name')
|
last = row.pop("order__customer__last_name")
|
||||||
if first is None:
|
if first is None:
|
||||||
who = self.EXTERNAL_USER
|
who = self.EXTERNAL_USER
|
||||||
else:
|
else:
|
||||||
who = '{} {}'.format(first, last)
|
who = "{} {}".format(first, last)
|
||||||
row['full_name'] = who
|
row["full_name"] = who
|
||||||
|
|
||||||
return per_user
|
return per_user
|
||||||
|
|
||||||
def get_counts(self, obj):
|
def get_counts(self, obj):
|
||||||
name_fields = ['order__customer__first_name', 'order__customer__last_name']
|
name_fields = ["order__customer__first_name", "order__customer__last_name"]
|
||||||
per_user = OrderLine.objects.filter(product=obj)
|
per_user = OrderLine.objects.filter(product=obj)
|
||||||
per_user = per_user.values(*name_fields).annotate(amount_total=Sum('amount')).order_by('-amount_total')
|
per_user = per_user.values(*name_fields).annotate(amount_total=Sum("amount")).order_by("-amount_total")
|
||||||
|
|
||||||
return self._format_counts(per_user)
|
return self._format_counts(per_user)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Product
|
model = Product
|
||||||
fields = ['id', 'name', 'description', 'image', 'inventory_amount', 'active', 'created', 'counts']
|
fields = [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
"image",
|
||||||
|
"inventory_amount",
|
||||||
|
"active",
|
||||||
|
"created",
|
||||||
|
"counts",
|
||||||
|
]
|
||||||
+2
-5
@@ -7,16 +7,13 @@ from main.models import Product
|
|||||||
from rest_framework.viewsets import GenericViewSet
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
|
||||||
|
|
||||||
class ProductViewSet(mixins.ListModelMixin,
|
class ProductViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
|
||||||
mixins.RetrieveModelMixin,
|
|
||||||
GenericViewSet):
|
|
||||||
serializer_class = ProductSerializer
|
serializer_class = ProductSerializer
|
||||||
queryset = Product.objects.all()
|
queryset = Product.objects.all()
|
||||||
|
|
||||||
@action(detail=False, methods=['get'])
|
@action(detail=False, methods=["get"])
|
||||||
def group_by_user(self, request):
|
def group_by_user(self, request):
|
||||||
products = self.get_queryset().filter(active=True)
|
products = self.get_queryset().filter(active=True)
|
||||||
serializer = ProductStatSerializer(products, many=True)
|
serializer = ProductStatSerializer(products, many=True)
|
||||||
|
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
+2
-2
@@ -2,8 +2,8 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
|
|
||||||
class BBSAppConfig(AppConfig):
|
class BBSAppConfig(AppConfig):
|
||||||
name = 'main'
|
name = "main"
|
||||||
verbose_name = 'Biceps Bar System'
|
verbose_name = "Biceps Bar System"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
# all models are loaded, could attach signals
|
# all models are loaded, could attach signals
|
||||||
|
|||||||
+15
-16
@@ -10,23 +10,23 @@ class OrderForm(forms.ModelForm):
|
|||||||
customer_name = forms.CharField(required=False)
|
customer_name = forms.CharField(required=False)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.cleaned_data['customer'] is None and self.cleaned_data['customer_name'] != "Ekstern":
|
if self.cleaned_data["customer"] is None and self.cleaned_data["customer_name"] != "Ekstern":
|
||||||
raise forms.ValidationError(_("Du har ikke valgt hvem som skal kjøpe."))
|
raise forms.ValidationError(_("Du har ikke valgt hvem som skal kjøpe."))
|
||||||
|
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
def clean_customer(self):
|
def clean_customer(self):
|
||||||
if self.cleaned_data['customer'] is None:
|
if self.cleaned_data["customer"] is None:
|
||||||
return self.cleaned_data['customer']
|
return self.cleaned_data["customer"]
|
||||||
|
|
||||||
if self.cleaned_data['customer'].profile.balance <= 0:
|
if self.cleaned_data["customer"].profile.balance <= 0:
|
||||||
raise forms.ValidationError(_("Du har negativ saldo og kan ikke kjøpe noe."))
|
raise forms.ValidationError(_("Du har negativ saldo og kan ikke kjøpe noe."))
|
||||||
|
|
||||||
return self.cleaned_data['customer']
|
return self.cleaned_data["customer"]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Order
|
model = Order
|
||||||
fields = ('customer',)
|
fields = ("customer",)
|
||||||
|
|
||||||
|
|
||||||
class OrderLineForm(forms.Form):
|
class OrderLineForm(forms.Form):
|
||||||
@@ -37,10 +37,9 @@ class OrderLineForm(forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class DepositForm(forms.ModelForm):
|
class DepositForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Transaction
|
model = Transaction
|
||||||
fields = ['user', 'amount']
|
fields = ["user", "amount"]
|
||||||
|
|
||||||
|
|
||||||
class SimpleCreateUserForm(forms.Form):
|
class SimpleCreateUserForm(forms.Form):
|
||||||
@@ -50,15 +49,15 @@ class SimpleCreateUserForm(forms.Form):
|
|||||||
facebook_username = forms.CharField(required=False, label="Facebook-brukernavn")
|
facebook_username = forms.CharField(required=False, label="Facebook-brukernavn")
|
||||||
|
|
||||||
def _get_facebook_url(self):
|
def _get_facebook_url(self):
|
||||||
if self.cleaned_data['facebook_username'] == "":
|
if self.cleaned_data["facebook_username"] == "":
|
||||||
return ""
|
return ""
|
||||||
return "https://graph.facebook.com/{0}/picture".format(self.cleaned_data['facebook_username'])
|
return "https://graph.facebook.com/{0}/picture".format(self.cleaned_data["facebook_username"])
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
username = format_username(self.cleaned_data['first_name'], self.cleaned_data['last_name'])
|
username = format_username(self.cleaned_data["first_name"], self.cleaned_data["last_name"])
|
||||||
user = User.objects.create_user(username, email=self.cleaned_data['email'])
|
user = User.objects.create_user(username, email=self.cleaned_data["email"])
|
||||||
user.first_name = self.cleaned_data['first_name']
|
user.first_name = self.cleaned_data["first_name"]
|
||||||
user.last_name = self.cleaned_data['last_name']
|
user.last_name = self.cleaned_data["last_name"]
|
||||||
|
|
||||||
profile = UserProfile.objects.create(user=user)
|
profile = UserProfile.objects.create(user=user)
|
||||||
profile.image = self._get_facebook_url()
|
profile.image = self._get_facebook_url()
|
||||||
@@ -76,11 +75,11 @@ class InventoryTransactionForm(forms.ModelForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# Only show active products
|
# Only show active products
|
||||||
super(InventoryTransactionForm, self).__init__(*args, **kwargs)
|
super(InventoryTransactionForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['product'].queryset = Product.objects.filter(active=True)
|
self.fields["product"].queryset = Product.objects.filter(active=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryTransaction
|
model = InventoryTransaction
|
||||||
fields = ['product', 'amount', 'unit_price', 'comment']
|
fields = ["product", "amount", "unit_price", "comment"]
|
||||||
|
|
||||||
|
|
||||||
class DateRangeForm(forms.Form):
|
class DateRangeForm(forms.Form):
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__author__ = 'Nikolai'
|
__author__ = "Nikolai"
|
||||||
@@ -1 +1 @@
|
|||||||
__author__ = 'Nikolai'
|
__author__ = "Nikolai"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
xfrom django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
import difflib
|
import difflib
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
@@ -13,16 +13,17 @@ class Command(BaseCommand):
|
|||||||
- Matches names against the local DB
|
- Matches names against the local DB
|
||||||
- Saves the users Facebook picture in his/her user profile.
|
- Saves the users Facebook picture in his/her user profile.
|
||||||
"""
|
"""
|
||||||
KAK_GROUP_ID = '108265352570560'
|
|
||||||
|
KAK_GROUP_ID = "108265352570560"
|
||||||
graph = None
|
graph = None
|
||||||
|
|
||||||
def get_kak_members(self):
|
def get_kak_members(self):
|
||||||
members = self.graph.get_connections(id=self.KAK_GROUP_ID, connection_name='members')
|
members = self.graph.get_connections(id=self.KAK_GROUP_ID, connection_name="members")
|
||||||
return [(member['name'], member['id']) for member in members['data']]
|
return [(member["name"], member["id"]) for member in members["data"]]
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
raise CommandError('Missing access token')
|
raise CommandError("Missing access token")
|
||||||
|
|
||||||
access_token = args[0]
|
access_token = args[0]
|
||||||
self.graph = facebook.GraphAPI(access_token=access_token)
|
self.graph = facebook.GraphAPI(access_token=access_token)
|
||||||
@@ -31,8 +32,8 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
names = [] # to fuzzy search for a name
|
names = [] # to fuzzy search for a name
|
||||||
name_lookup = {} # to update the matching UserProfile object
|
name_lookup = {} # to update the matching UserProfile object
|
||||||
for u in User.objects.all().order_by('id'):
|
for u in User.objects.all().order_by("id"):
|
||||||
fullname = u'{} {}'.format(u.first_name, u.last_name)
|
fullname = "{} {}".format(u.first_name, u.last_name)
|
||||||
names.append(fullname)
|
names.append(fullname)
|
||||||
name_lookup[fullname] = u.id
|
name_lookup[fullname] = u.id
|
||||||
|
|
||||||
@@ -41,6 +42,5 @@ class Command(BaseCommand):
|
|||||||
if match:
|
if match:
|
||||||
u = UserProfile.objects.get(pk=name_lookup[match[0]])
|
u = UserProfile.objects.get(pk=name_lookup[match[0]])
|
||||||
if str(u.image) is "":
|
if str(u.image) is "":
|
||||||
u.image = 'https://graph.facebook.com/{0}/picture'.format(member[1])
|
u.image = "https://graph.facebook.com/{0}/picture".format(member[1])
|
||||||
u.save()
|
u.save()
|
||||||
|
|
||||||
+139
-52
@@ -10,89 +10,176 @@ class Migration(migrations.Migration):
|
|||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='InventoryTransaction',
|
name="InventoryTransaction",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('amount', models.FloatField(verbose_name='Antall')),
|
"id",
|
||||||
('unit_price', models.FloatField(verbose_name='Enhetspris')),
|
models.AutoField(
|
||||||
('comment', models.TextField(verbose_name='Kommentar', blank=True)),
|
verbose_name="ID",
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
serialize=False,
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("amount", models.FloatField(verbose_name="Antall")),
|
||||||
|
("unit_price", models.FloatField(verbose_name="Enhetspris")),
|
||||||
|
("comment", models.TextField(verbose_name="Kommentar", blank=True)),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Order',
|
name="Order",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('order_sum', models.FloatField(db_column='sum')),
|
"id",
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
models.AutoField(
|
||||||
('customer', models.ForeignKey(related_name='orders', on_delete=models.CASCADE, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
|
verbose_name="ID",
|
||||||
|
serialize=False,
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("order_sum", models.FloatField(db_column="sum")),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
(
|
||||||
|
"customer",
|
||||||
|
models.ForeignKey(
|
||||||
|
related_name="orders",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
blank=True,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='OrderLine',
|
name="OrderLine",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('amount', models.IntegerField()),
|
"id",
|
||||||
('unit_price', models.FloatField(verbose_name='Enhetspris')),
|
models.AutoField(
|
||||||
('order', models.ForeignKey(on_delete=models.CASCADE, related_name='orderlines', to='main.Order')),
|
verbose_name="ID",
|
||||||
|
serialize=False,
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("amount", models.IntegerField()),
|
||||||
|
("unit_price", models.FloatField(verbose_name="Enhetspris")),
|
||||||
|
(
|
||||||
|
"order",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="orderlines",
|
||||||
|
to="main.Order",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Product',
|
name="Product",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('name', models.CharField(max_length=64)),
|
"id",
|
||||||
('sale_price_int', models.FloatField()),
|
models.AutoField(
|
||||||
('sale_price_ext', models.FloatField()),
|
verbose_name="ID",
|
||||||
('description', models.CharField(max_length=256, blank=True)),
|
serialize=False,
|
||||||
('volume_liter', models.FloatField(null=True)),
|
auto_created=True,
|
||||||
('alcohol_percent', models.FloatField(null=True)),
|
primary_key=True,
|
||||||
('image', models.ImageField(upload_to='products', blank=True)),
|
),
|
||||||
('customer_support', models.CharField(max_length=32, blank=True)),
|
),
|
||||||
('inventory_amount', models.FloatField(default=0)),
|
("name", models.CharField(max_length=64)),
|
||||||
('active', models.BooleanField(default=True)),
|
("sale_price_int", models.FloatField()),
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
("sale_price_ext", models.FloatField()),
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
("description", models.CharField(max_length=256, blank=True)),
|
||||||
|
("volume_liter", models.FloatField(null=True)),
|
||||||
|
("alcohol_percent", models.FloatField(null=True)),
|
||||||
|
("image", models.ImageField(upload_to="products", blank=True)),
|
||||||
|
("customer_support", models.CharField(max_length=32, blank=True)),
|
||||||
|
("inventory_amount", models.FloatField(default=0)),
|
||||||
|
("active", models.BooleanField(default=True)),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['name'],
|
"ordering": ["name"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Transaction',
|
name="Transaction",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('amount', models.FloatField()),
|
"id",
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
models.AutoField(
|
||||||
('user', models.ForeignKey(on_delete=models.CASCADE, related_name='transactions', to=settings.AUTH_USER_MODEL)),
|
verbose_name="ID",
|
||||||
|
serialize=False,
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("amount", models.FloatField()),
|
||||||
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="transactions",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='UserProfile',
|
name="UserProfile",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('balance', models.FloatField(default=0.0)),
|
"id",
|
||||||
('image', models.URLField(blank=True)),
|
models.AutoField(
|
||||||
('user', models.OneToOneField(on_delete=models.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
|
verbose_name="ID",
|
||||||
|
serialize=False,
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("balance", models.FloatField(default=0.0)),
|
||||||
|
("image", models.URLField(blank=True)),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.OneToOneField(
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="profile",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='orderline',
|
model_name="orderline",
|
||||||
name='product',
|
name="product",
|
||||||
field=models.ForeignKey(on_delete=models.CASCADE, related_name='orderlines', to='main.Product'),
|
field=models.ForeignKey(on_delete=models.CASCADE, related_name="orderlines", to="main.Product"),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='inventorytransaction',
|
model_name="inventorytransaction",
|
||||||
name='product',
|
name="product",
|
||||||
field=models.ForeignKey(on_delete=models.CASCADE, related_name='transactions', verbose_name='Produkt', to='main.Product'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="transactions",
|
||||||
|
verbose_name="Produkt",
|
||||||
|
to="main.Product",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='inventorytransaction',
|
model_name="inventorytransaction",
|
||||||
name='user',
|
name="user",
|
||||||
field=models.ForeignKey(on_delete=models.CASCADE, related_name='inventory_transactions', to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="inventory_transactions",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='orderline',
|
name="orderline",
|
||||||
unique_together=set([('order', 'product')]),
|
unique_together=set([("order", "product")]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
+42
-35
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -6,7 +5,6 @@ from django.db import models
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Product(models.Model):
|
class Product(models.Model):
|
||||||
name = models.CharField(max_length=64)
|
name = models.CharField(max_length=64)
|
||||||
sale_price_int = models.FloatField()
|
sale_price_int = models.FloatField()
|
||||||
@@ -14,7 +12,7 @@ class Product(models.Model):
|
|||||||
description = models.CharField(max_length=256, blank=True)
|
description = models.CharField(max_length=256, blank=True)
|
||||||
volume_liter = models.FloatField(null=True)
|
volume_liter = models.FloatField(null=True)
|
||||||
alcohol_percent = models.FloatField(null=True)
|
alcohol_percent = models.FloatField(null=True)
|
||||||
image = models.ImageField(upload_to='products', blank=True)
|
image = models.ImageField(upload_to="products", blank=True)
|
||||||
customer_support = models.CharField(max_length=32, blank=True)
|
customer_support = models.CharField(max_length=32, blank=True)
|
||||||
inventory_amount = models.FloatField(default=0)
|
inventory_amount = models.FloatField(default=0)
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
@@ -29,15 +27,15 @@ class Product(models.Model):
|
|||||||
if self.inventory_amount <= 0:
|
if self.inventory_amount <= 0:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
unit_prices = self.transactions.order_by('created').values('amount', 'unit_price')
|
unit_prices = self.transactions.order_by("created").values("amount", "unit_price")
|
||||||
sum_units = 0
|
sum_units = 0
|
||||||
for up in unit_prices:
|
for up in unit_prices:
|
||||||
sum_units += up['amount']
|
sum_units += up["amount"]
|
||||||
if self.inventory_amount <= sum_units:
|
if self.inventory_amount <= sum_units:
|
||||||
return up['unit_price']
|
return up["unit_price"]
|
||||||
|
|
||||||
if len(unit_prices) > 0:
|
if len(unit_prices) > 0:
|
||||||
return unit_prices[0]['unit_price']
|
return unit_prices[0]["unit_price"]
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -49,13 +47,18 @@ class Product(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ["name"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Order(models.Model):
|
class Order(models.Model):
|
||||||
customer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True, related_name='orders')
|
customer = models.ForeignKey(
|
||||||
order_sum = models.FloatField(db_column='sum')
|
settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name="orders",
|
||||||
|
)
|
||||||
|
order_sum = models.FloatField(db_column="sum")
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
def is_external(self):
|
def is_external(self):
|
||||||
@@ -68,12 +71,11 @@ class Order(models.Model):
|
|||||||
return "{}: {} kr".format(self.customer or "Ekstern", self.order_sum)
|
return "{}: {} kr".format(self.customer or "Ekstern", self.order_sum)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class OrderLine(models.Model):
|
class OrderLine(models.Model):
|
||||||
order = models.ForeignKey('main.Order', on_delete=models.CASCADE, related_name='orderlines')
|
order = models.ForeignKey("main.Order", on_delete=models.CASCADE, related_name="orderlines")
|
||||||
product = models.ForeignKey('main.Product', on_delete=models.CASCADE, related_name='orderlines')
|
product = models.ForeignKey("main.Product", on_delete=models.CASCADE, related_name="orderlines")
|
||||||
amount = models.IntegerField()
|
amount = models.IntegerField()
|
||||||
unit_price = models.FloatField(verbose_name=_('Enhetspris'))
|
unit_price = models.FloatField(verbose_name=_("Enhetspris"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def price(self):
|
def price(self):
|
||||||
@@ -83,13 +85,13 @@ class OrderLine(models.Model):
|
|||||||
return "{} {} ({} kr per)".format(self.amount, self.product, self.unit_price)
|
return "{} {} ({} kr per)".format(self.amount, self.product, self.unit_price)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('order', 'product')
|
unique_together = ("order", "product")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Transaction(models.Model):
|
class Transaction(models.Model):
|
||||||
""" Money """
|
"""Money"""
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='transactions')
|
|
||||||
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="transactions")
|
||||||
amount = models.FloatField()
|
amount = models.FloatField()
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
@@ -97,14 +99,23 @@ class Transaction(models.Model):
|
|||||||
return "{}: {}: {}".format(self.id, self.user, self.amount)
|
return "{}: {}: {}".format(self.id, self.user, self.amount)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryTransaction(models.Model):
|
class InventoryTransaction(models.Model):
|
||||||
""" Goods """
|
"""Goods"""
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='inventory_transactions')
|
|
||||||
product = models.ForeignKey('main.Product', on_delete=models.CASCADE, related_name='transactions', verbose_name=_('Produkt'))
|
user = models.ForeignKey(
|
||||||
amount = models.FloatField(verbose_name=_('Antall'))
|
settings.AUTH_USER_MODEL,
|
||||||
unit_price = models.FloatField(verbose_name=_('Enhetspris'))
|
on_delete=models.CASCADE,
|
||||||
comment = models.TextField(verbose_name=_('Kommentar'), blank=True)
|
related_name="inventory_transactions",
|
||||||
|
)
|
||||||
|
product = models.ForeignKey(
|
||||||
|
"main.Product",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="transactions",
|
||||||
|
verbose_name=_("Produkt"),
|
||||||
|
)
|
||||||
|
amount = models.FloatField(verbose_name=_("Antall"))
|
||||||
|
unit_price = models.FloatField(verbose_name=_("Enhetspris"))
|
||||||
|
comment = models.TextField(verbose_name=_("Kommentar"), blank=True)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -113,16 +124,12 @@ class InventoryTransaction(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{}: {}: {}. {:.0f} stykker ({} kr per)".format(
|
return "{}: {}: {}. {:.0f} stykker ({} kr per)".format(
|
||||||
self.id,
|
self.id, self.user, self.product, self.amount, self.unit_price
|
||||||
self.user,
|
)
|
||||||
self.product,
|
|
||||||
self.amount,
|
|
||||||
self.unit_price)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserProfile(models.Model):
|
class UserProfile(models.Model):
|
||||||
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile")
|
||||||
balance = models.FloatField(default=0.0)
|
balance = models.FloatField(default=0.0)
|
||||||
image = models.URLField(blank=True)
|
image = models.URLField(blank=True)
|
||||||
|
|
||||||
@@ -130,7 +137,7 @@ class UserProfile(models.Model):
|
|||||||
if len(self.user.orders.all()) == 0:
|
if len(self.user.orders.all()) == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.user.orders.order_by('-created')[0]
|
return self.user.orders.order_by("-created")[0]
|
||||||
|
|
||||||
def last_purchase_date(self):
|
def last_purchase_date(self):
|
||||||
last = self.last_purchase()
|
last = self.last_purchase()
|
||||||
@@ -141,7 +148,7 @@ class UserProfile(models.Model):
|
|||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
|
||||||
if not self.image:
|
if not self.image:
|
||||||
return static('dist/images/unknown_person.png')
|
return static("dist/images/unknown_person.png")
|
||||||
|
|
||||||
return self.image
|
return self.image
|
||||||
|
|
||||||
|
|||||||
+44
-26
@@ -2,46 +2,64 @@ from django.conf.urls import url
|
|||||||
from django.contrib.staticfiles.views import serve
|
from django.contrib.staticfiles.views import serve
|
||||||
|
|
||||||
from main.api.views import ProductViewSet
|
from main.api.views import ProductViewSet
|
||||||
from main.views.stats import (stats_list, stats_products_realtime, stats_products_per_user,
|
from main.views.stats import (
|
||||||
OrdersHourlyView, OrdersDailyView, OrdersMonthlyView, OrdersYearlyView)
|
stats_list,
|
||||||
|
stats_products_realtime,
|
||||||
|
stats_products_per_user,
|
||||||
|
OrdersHourlyView,
|
||||||
|
OrdersDailyView,
|
||||||
|
OrdersMonthlyView,
|
||||||
|
OrdersYearlyView,
|
||||||
|
)
|
||||||
from main.views.general import register, deposit, log
|
from main.views.general import register, deposit, log
|
||||||
from main.views.reports import inventory, inventory_add, report
|
from main.views.reports import inventory, inventory_add, report
|
||||||
from main.views.users import create_user, profile
|
from main.views.users import create_user, profile
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', register, name='home'),
|
url(r"^$", register, name="home"),
|
||||||
url(r'^register/$', register, name='register'),
|
url(r"^register/$", register, name="register"),
|
||||||
url(r'^deposit/$', deposit, name='deposit'),
|
url(r"^deposit/$", deposit, name="deposit"),
|
||||||
url(r'^log/$', log, name='log'),
|
url(r"^log/$", log, name="log"),
|
||||||
url(r'^log/all$', log, kwargs={'limit': None}, name='log-all'),
|
url(r"^log/all$", log, kwargs={"limit": None}, name="log-all"),
|
||||||
|
|
||||||
# Users
|
# Users
|
||||||
url(r'^user/create/$', create_user, name='create-user'),
|
url(r"^user/create/$", create_user, name="create-user"),
|
||||||
url(r'^profile/$', profile, name='profile'),
|
url(r"^profile/$", profile, name="profile"),
|
||||||
|
|
||||||
# Reports
|
# Reports
|
||||||
url(r'^inventory/$', inventory, name='inventory'),
|
url(r"^inventory/$", inventory, name="inventory"),
|
||||||
url(r'^inventory/add/$', inventory_add, name='inventory-add'),
|
url(r"^inventory/add/$", inventory_add, name="inventory-add"),
|
||||||
url(r'^report$', report, name='report'),
|
url(r"^report$", report, name="report"),
|
||||||
|
|
||||||
]
|
]
|
||||||
# Stats
|
# Stats
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
url(r'^stats/$', stats_list, name='stats'),
|
url(r"^stats/$", stats_list, name="stats"),
|
||||||
url(r'^stats/orders/hourly/$', OrdersHourlyView.as_view(), name='stats-orders-hourly'),
|
url(
|
||||||
url(r'^stats/orders/daily/', OrdersDailyView.as_view(), name='stats-orders-daily'),
|
r"^stats/orders/hourly/$",
|
||||||
url(r'^stats/orders/monthly/', OrdersMonthlyView.as_view(), name='stats-orders-monthly'),
|
OrdersHourlyView.as_view(),
|
||||||
url(r'^stats/orders/yearly/', OrdersYearlyView.as_view(), name='stats-orders-yearly'),
|
name="stats-orders-hourly",
|
||||||
url(r'^stats/orders/products_realtime$', stats_products_realtime, name='stats-products-realtime'),
|
),
|
||||||
url(r'^stats/products/by_user/(?P<user_id>[0-9]+)/$', stats_products_per_user, name='stats-products-per-user'),
|
url(r"^stats/orders/daily/", OrdersDailyView.as_view(), name="stats-orders-daily"),
|
||||||
|
url(
|
||||||
|
r"^stats/orders/monthly/",
|
||||||
|
OrdersMonthlyView.as_view(),
|
||||||
|
name="stats-orders-monthly",
|
||||||
|
),
|
||||||
|
url(r"^stats/orders/yearly/", OrdersYearlyView.as_view(), name="stats-orders-yearly"),
|
||||||
|
url(
|
||||||
|
r"^stats/orders/products_realtime$",
|
||||||
|
stats_products_realtime,
|
||||||
|
name="stats-products-realtime",
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r"^stats/products/by_user/(?P<user_id>[0-9]+)/$",
|
||||||
|
stats_products_per_user,
|
||||||
|
name="stats-products-per-user",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
# Root level service-worker.js
|
# Root level service-worker.js
|
||||||
urlpatterns += [
|
urlpatterns += [url(r"^service-worker\.js$", serve, kwargs={"path": "dist/service-worker.js"})]
|
||||||
url(r'^service-worker\.js$', serve, kwargs={'path': 'dist/service-worker.js'})
|
|
||||||
]
|
|
||||||
|
|
||||||
# API
|
# API
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'api/products', ProductViewSet)
|
router.register(r"api/products", ProductViewSet)
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls
|
||||||
+6
-8
@@ -1,25 +1,23 @@
|
|||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
|
|
||||||
def format_username(firstname, lastname):
|
def format_username(firstname, lastname):
|
||||||
first = firstname.lower().strip().replace(" ", "")[:6].encode('ascii', 'ignore').decode('utf-8')
|
first = firstname.lower().strip().replace(" ", "")[:6].encode("ascii", "ignore").decode("utf-8")
|
||||||
last = lastname.lower().strip().replace(" ", "")[:6].encode('ascii', 'ignore').decode('utf-8')
|
last = lastname.lower().strip().replace(" ", "")[:6].encode("ascii", "ignore").decode("utf-8")
|
||||||
|
|
||||||
return "{0}{1}".format(first, last)
|
return "{0}{1}".format(first, last)
|
||||||
|
|
||||||
|
|
||||||
def users_with_perm(perm_name):
|
def users_with_perm(perm_name):
|
||||||
return User.objects.filter(
|
return User.objects.filter(
|
||||||
Q(is_superuser=True) |
|
Q(is_superuser=True) | Q(user_permissions__codename=perm_name) | Q(groups__permissions__codename=perm_name)
|
||||||
Q(user_permissions__codename=perm_name) |
|
).distinct()
|
||||||
Q(groups__permissions__codename=perm_name)).distinct()
|
|
||||||
|
|
||||||
|
|
||||||
def users_format_js(users):
|
def users_format_js(users):
|
||||||
users_js = []
|
users_js = []
|
||||||
for user in users:
|
for user in users:
|
||||||
users_js.append('"' + user.first_name + ' ' + user.last_name[:1] + '"')
|
users_js.append('"' + user.first_name + " " + user.last_name[:1] + '"')
|
||||||
users_js = "[{}]".format(u",".join(users_js))
|
users_js = "[{}]".format(",".join(users_js))
|
||||||
return users_js
|
return users_js
|
||||||
+60
-45
@@ -16,7 +16,7 @@ from main.utils import users_format_js, users_with_perm
|
|||||||
|
|
||||||
def register(request):
|
def register(request):
|
||||||
products = Product.objects.filter(active=True)
|
products = Product.objects.filter(active=True)
|
||||||
cheapest_product_price = list(products.aggregate(Min('sale_price_int')).values())[0] or 0
|
cheapest_product_price = list(products.aggregate(Min("sale_price_int")).values())[0] or 0
|
||||||
|
|
||||||
half_a_year_ago = datetime.now() - timedelta(days=180)
|
half_a_year_ago = datetime.now() - timedelta(days=180)
|
||||||
users = User.objects.filter(profile__balance__gte=cheapest_product_price)
|
users = User.objects.filter(profile__balance__gte=cheapest_product_price)
|
||||||
@@ -34,24 +34,28 @@ def register(request):
|
|||||||
order = orderform.save(commit=False)
|
order = orderform.save(commit=False)
|
||||||
|
|
||||||
if order.is_external():
|
if order.is_external():
|
||||||
order_sum = sum([ol['amount'] * ol['unit_price_ext'] for ol in formset.cleaned_data])
|
order_sum = sum([ol["amount"] * ol["unit_price_ext"] for ol in formset.cleaned_data])
|
||||||
else:
|
else:
|
||||||
order_sum = sum([ol['amount'] * ol['unit_price_int'] for ol in formset.cleaned_data])
|
order_sum = sum([ol["amount"] * ol["unit_price_int"] for ol in formset.cleaned_data])
|
||||||
|
|
||||||
# empty order?
|
# empty order?
|
||||||
if order_sum == 0:
|
if order_sum == 0:
|
||||||
messages.error(request, 'Du har ikke valgt hva du skal kjøpe...')
|
messages.error(request, "Du har ikke valgt hva du skal kjøpe...")
|
||||||
return HttpResponseRedirect(reverse('register'))
|
return HttpResponseRedirect(reverse("register"))
|
||||||
|
|
||||||
if not order.is_external():
|
if not order.is_external():
|
||||||
profile = order.customer.profile
|
profile = order.customer.profile
|
||||||
# can she afford it?
|
# can she afford it?
|
||||||
if profile.balance < order_sum:
|
if profile.balance < order_sum:
|
||||||
messages.error(request, '{0} {1} har ikke råd, mangler {2} kr.'.format(
|
messages.error(
|
||||||
|
request,
|
||||||
|
"{0} {1} har ikke råd, mangler {2} kr.".format(
|
||||||
order.customer.first_name,
|
order.customer.first_name,
|
||||||
order.customer.last_name,
|
order.customer.last_name,
|
||||||
int(order_sum - profile.balance)))
|
int(order_sum - profile.balance),
|
||||||
return HttpResponseRedirect(reverse('register'))
|
),
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(reverse("register"))
|
||||||
# substract order from balance
|
# substract order from balance
|
||||||
profile.balance -= order_sum
|
profile.balance -= order_sum
|
||||||
profile.save()
|
profile.save()
|
||||||
@@ -61,106 +65,117 @@ def register(request):
|
|||||||
|
|
||||||
orderlines = []
|
orderlines = []
|
||||||
for ol in formset.cleaned_data:
|
for ol in formset.cleaned_data:
|
||||||
if ol['amount'] > 0:
|
if ol["amount"] > 0:
|
||||||
product = Product.objects.get(pk=ol['product'])
|
product = Product.objects.get(pk=ol["product"])
|
||||||
|
|
||||||
if order.is_external():
|
if order.is_external():
|
||||||
unit_price = ol['unit_price_ext']
|
unit_price = ol["unit_price_ext"]
|
||||||
else:
|
else:
|
||||||
unit_price = ol['unit_price_int']
|
unit_price = ol["unit_price_int"]
|
||||||
|
|
||||||
OrderLine.objects.create(
|
OrderLine.objects.create(
|
||||||
order=order,
|
order=order,
|
||||||
product=product,
|
product=product,
|
||||||
amount=ol['amount'],
|
amount=ol["amount"],
|
||||||
unit_price=unit_price)
|
unit_price=unit_price,
|
||||||
|
)
|
||||||
|
|
||||||
product.inventory_amount -= ol['amount']
|
product.inventory_amount -= ol["amount"]
|
||||||
product.save()
|
product.save()
|
||||||
|
|
||||||
orderlines.append("{0} {1}".format(ol['amount'], product))
|
orderlines.append("{0} {1}".format(ol["amount"], product))
|
||||||
|
|
||||||
if order.is_external():
|
if order.is_external():
|
||||||
messages.success(request, 'Ekstern kjøpte {0}.'.format(", ".join(orderlines)))
|
messages.success(request, "Ekstern kjøpte {0}.".format(", ".join(orderlines)))
|
||||||
else:
|
else:
|
||||||
messages.success(request, '{0} {1} kjøpte {2}.'.format(
|
messages.success(
|
||||||
|
request,
|
||||||
|
"{0} {1} kjøpte {2}.".format(
|
||||||
order.customer.first_name,
|
order.customer.first_name,
|
||||||
order.customer.last_name,
|
order.customer.last_name,
|
||||||
", ".join(orderlines)
|
", ".join(orderlines),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
|
|
||||||
return redirect('register')
|
return redirect("register")
|
||||||
else:
|
else:
|
||||||
# TODO specify error(s)
|
# TODO specify error(s)
|
||||||
messages.error(request, 'Skjemaet er ikke gyldig.')
|
messages.error(request, "Skjemaet er ikke gyldig.")
|
||||||
orderform = OrderForm(request.POST)
|
orderform = OrderForm(request.POST)
|
||||||
formset = OrderLineFormSet(request.POST)
|
formset = OrderLineFormSet(request.POST)
|
||||||
else:
|
else:
|
||||||
orderform = OrderForm()
|
orderform = OrderForm()
|
||||||
formset = OrderLineFormSet()
|
formset = OrderLineFormSet()
|
||||||
|
|
||||||
return render(request, 'register.html', locals())
|
return render(request, "register.html", locals())
|
||||||
|
|
||||||
|
|
||||||
def deposit(request):
|
def deposit(request):
|
||||||
users = User.objects.all().order_by('first_name', 'last_name')
|
users = User.objects.all().order_by("first_name", "last_name")
|
||||||
users_js = users_format_js(users)
|
users_js = users_format_js(users)
|
||||||
allowed_users = users_with_perm('add_transaction')
|
allowed_users = users_with_perm("add_transaction")
|
||||||
error_message_template = '{0} {1} kan ikke sette inn {2} kr, det overskrider maks saldo ({3} kr) med {4} kr'
|
error_message_template = "{0} {1} kan ikke sette inn {2} kr, det overskrider maks saldo ({3} kr) med {4} kr"
|
||||||
|
|
||||||
limit_deposists = settings.BBS_LIMIT_DEPOSITS
|
limit_deposists = settings.BBS_LIMIT_DEPOSITS
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = DepositForm(request.POST)
|
form = DepositForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
amount = form.cleaned_data['amount']
|
amount = form.cleaned_data["amount"]
|
||||||
user = form.cleaned_data['user']
|
user = form.cleaned_data["user"]
|
||||||
if amount + user.profile.balance > settings.BBS_SALDO_MAX and amount + user.profile.balance != 1337:
|
if amount + user.profile.balance > settings.BBS_SALDO_MAX and amount + user.profile.balance != 1337:
|
||||||
messages.error(request, error_message_template.format(
|
messages.error(
|
||||||
|
request,
|
||||||
|
error_message_template.format(
|
||||||
user.first_name,
|
user.first_name,
|
||||||
user.last_name,
|
user.last_name,
|
||||||
amount,
|
amount,
|
||||||
settings.BBS_SALDO_MAX,
|
settings.BBS_SALDO_MAX,
|
||||||
amount + user.profile.balance - settings.BBS_SALDO_MAX
|
amount + user.profile.balance - settings.BBS_SALDO_MAX,
|
||||||
))
|
),
|
||||||
return HttpResponseRedirect(reverse('deposit'))
|
)
|
||||||
|
return HttpResponseRedirect(reverse("deposit"))
|
||||||
|
|
||||||
transaction = form.save()
|
transaction = form.save()
|
||||||
profile = transaction.user.profile
|
profile = transaction.user.profile
|
||||||
profile.balance += transaction.amount
|
profile.balance += transaction.amount
|
||||||
profile.save()
|
profile.save()
|
||||||
|
|
||||||
messages.success(request, '{0} {1} satte inn {2} kr. Ny saldo er {3}'.format(
|
messages.success(
|
||||||
|
request,
|
||||||
|
"{0} {1} satte inn {2} kr. Ny saldo er {3}".format(
|
||||||
transaction.user.first_name,
|
transaction.user.first_name,
|
||||||
transaction.user.last_name,
|
transaction.user.last_name,
|
||||||
transaction.amount,
|
transaction.amount,
|
||||||
profile.balance))
|
profile.balance,
|
||||||
return HttpResponseRedirect(reverse('deposit'))
|
),
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(reverse("deposit"))
|
||||||
else:
|
else:
|
||||||
messages.error(request, 'Skjemaet er ikke gyldig.')
|
messages.error(request, "Skjemaet er ikke gyldig.")
|
||||||
form = DepositForm(request.POST)
|
form = DepositForm(request.POST)
|
||||||
else:
|
else:
|
||||||
form = DepositForm()
|
form = DepositForm()
|
||||||
|
|
||||||
return render(request, 'deposit.html', locals())
|
return render(request, "deposit.html", locals())
|
||||||
|
|
||||||
|
|
||||||
def log(request, limit=datetime.now()-timedelta(days=2)):
|
def log(request, limit=datetime.now() - timedelta(days=2)):
|
||||||
if limit:
|
if limit:
|
||||||
# display last two days of orders and transactions
|
# display last two days of orders and transactions
|
||||||
orders = Order.objects.filter(created__gte=limit).order_by('-created')
|
orders = Order.objects.filter(created__gte=limit).order_by("-created")
|
||||||
transactions = Transaction.objects.filter(created__gte=limit).order_by('-created')
|
transactions = Transaction.objects.filter(created__gte=limit).order_by("-created")
|
||||||
|
|
||||||
# no action in the last two days?
|
# no action in the last two days?
|
||||||
# ...then display the last 5 items
|
# ...then display the last 5 items
|
||||||
if len(orders) == 0:
|
if len(orders) == 0:
|
||||||
orders = Order.objects.all().order_by('-created')[:5]
|
orders = Order.objects.all().order_by("-created")[:5]
|
||||||
|
|
||||||
if len(transactions) == 0:
|
if len(transactions) == 0:
|
||||||
transactions = Transaction.objects.all().order_by('-created')[:5]
|
transactions = Transaction.objects.all().order_by("-created")[:5]
|
||||||
else:
|
else:
|
||||||
# display everything
|
# display everything
|
||||||
orders = Order.objects.all().order_by('-created')
|
orders = Order.objects.all().order_by("-created")
|
||||||
transactions = Transaction.objects.all().order_by('-created')
|
transactions = Transaction.objects.all().order_by("-created")
|
||||||
|
|
||||||
return render(request, 'log.html', locals())
|
return render(request, "log.html", locals())
|
||||||
+54
-46
@@ -16,58 +16,66 @@ def report(request):
|
|||||||
# end of day
|
# end of day
|
||||||
end_time = datetime.now().replace(hour=23, minute=59, second=59, microsecond=999)
|
end_time = datetime.now().replace(hour=23, minute=59, second=59, microsecond=999)
|
||||||
|
|
||||||
if request.GET.get('start_time', False) or request.GET.get('end_time', False):
|
if request.GET.get("start_time", False) or request.GET.get("end_time", False):
|
||||||
form = DateRangeForm(request.GET)
|
form = DateRangeForm(request.GET)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
start_time = form.cleaned_data['start_time']
|
start_time = form.cleaned_data["start_time"]
|
||||||
# end of day
|
# end of day
|
||||||
end_time = form.cleaned_data['end_time'].replace(hour=23, minute=59, second=59, microsecond=999)
|
end_time = form.cleaned_data["end_time"].replace(hour=23, minute=59, second=59, microsecond=999)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = DateRangeForm(initial={
|
form = DateRangeForm(
|
||||||
'start_time': start_time.strftime("%Y-%m-%d"),
|
initial={
|
||||||
'end_time': end_time.strftime("%Y-%m-%d")
|
"start_time": start_time.strftime("%Y-%m-%d"),
|
||||||
})
|
"end_time": end_time.strftime("%Y-%m-%d"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Inventory
|
# Inventory
|
||||||
products = Product.objects\
|
products = (
|
||||||
.filter(transactions__created__range=[start_time, end_time])\
|
Product.objects.filter(transactions__created__range=[start_time, end_time])
|
||||||
.values('name', 'transactions__unit_price', 'transactions__amount')\
|
.values("name", "transactions__unit_price", "transactions__amount")
|
||||||
.order_by('name')
|
.order_by("name")
|
||||||
|
)
|
||||||
# group by name and sum
|
# group by name and sum
|
||||||
inventory_products = {}
|
inventory_products = {}
|
||||||
for k, g in groupby(products, lambda x: x['name']):
|
for k, g in groupby(products, lambda x: x["name"]):
|
||||||
group = []
|
group = []
|
||||||
for el in g:
|
for el in g:
|
||||||
el['transactions__price'] = el['transactions__amount'] * el['transactions__unit_price']
|
el["transactions__price"] = el["transactions__amount"] * el["transactions__unit_price"]
|
||||||
group.append(el)
|
group.append(el)
|
||||||
inventory_products.update({k: {
|
inventory_products.update(
|
||||||
'name': k,
|
{
|
||||||
|
k: {
|
||||||
|
"name": k,
|
||||||
"transactions": group,
|
"transactions": group,
|
||||||
"transactions_sum": sum(map(lambda x: x['transactions__price'], group)),
|
"transactions_sum": sum(map(lambda x: x["transactions__price"], group)),
|
||||||
"transactions_units": sum(map(lambda x: x['transactions__amount'], group))
|
"transactions_units": sum(map(lambda x: x["transactions__amount"], group)),
|
||||||
}})
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
itrans = InventoryTransaction.objects.filter(created__range=[start_time, end_time])
|
itrans = InventoryTransaction.objects.filter(created__range=[start_time, end_time])
|
||||||
inv_in = sum(map(lambda x: x[1].get('transactions_sum', 0), inventory_products.items()))
|
inv_in = sum(map(lambda x: x[1].get("transactions_sum", 0), inventory_products.items()))
|
||||||
|
|
||||||
# out
|
# out
|
||||||
out_products = Product.objects\
|
out_products = (
|
||||||
.filter(orderlines__order__created__range=[start_time, end_time])\
|
Product.objects.filter(orderlines__order__created__range=[start_time, end_time])
|
||||||
.values('name', 'orderlines__unit_price', 'orderlines__amount')\
|
.values("name", "orderlines__unit_price", "orderlines__amount")
|
||||||
.order_by('name')
|
.order_by("name")
|
||||||
|
)
|
||||||
# group by name and sum
|
# group by name and sum
|
||||||
for k, g in groupby(out_products, lambda x: x['name']):
|
for k, g in groupby(out_products, lambda x: x["name"]):
|
||||||
group = []
|
group = []
|
||||||
for el in g:
|
for el in g:
|
||||||
el['orderlines__price'] = el['orderlines__amount'] * el['orderlines__unit_price']
|
el["orderlines__price"] = el["orderlines__amount"] * el["orderlines__unit_price"]
|
||||||
group.append(el)
|
group.append(el)
|
||||||
|
|
||||||
product = {
|
product = {
|
||||||
'name': k,
|
"name": k,
|
||||||
"orderlines": group,
|
"orderlines": group,
|
||||||
"orderlines_sum": sum(map(lambda x: x['orderlines__price'], group)),
|
"orderlines_sum": sum(map(lambda x: x["orderlines__price"], group)),
|
||||||
"orderlines_units": sum(map(lambda x: x['orderlines__amount'], group))
|
"orderlines_units": sum(map(lambda x: x["orderlines__amount"], group)),
|
||||||
}
|
}
|
||||||
if k in inventory_products:
|
if k in inventory_products:
|
||||||
# add diff
|
# add diff
|
||||||
@@ -76,42 +84,42 @@ def report(request):
|
|||||||
inventory_products.update({k: product})
|
inventory_products.update({k: product})
|
||||||
|
|
||||||
for p in inventory_products.items():
|
for p in inventory_products.items():
|
||||||
p[1]['value_diff'] = p[1].get('transactions_sum', 0) - p[1].get('orderlines_sum', 0)
|
p[1]["value_diff"] = p[1].get("transactions_sum", 0) - p[1].get("orderlines_sum", 0)
|
||||||
p[1]['units_diff'] = p[1].get('transactions_units', 0) - p[1].get('orderlines_units', 0)
|
p[1]["units_diff"] = p[1].get("transactions_units", 0) - p[1].get("orderlines_units", 0)
|
||||||
|
|
||||||
inv_out = sum(map(lambda x: x[1].get('orderlines_sum', 0), inventory_products.items()))
|
inv_out = sum(map(lambda x: x[1].get("orderlines_sum", 0), inventory_products.items()))
|
||||||
|
|
||||||
inv_diff = inv_in - inv_out
|
inv_diff = inv_in - inv_out
|
||||||
|
|
||||||
# Deposits and sales
|
# Deposits and sales
|
||||||
saldo_profiles = sum(UserProfile.objects.all().values_list('balance', flat=True))
|
saldo_profiles = sum(UserProfile.objects.all().values_list("balance", flat=True))
|
||||||
deposits = sum(Transaction.objects.all().values_list('amount', flat=True))
|
deposits = sum(Transaction.objects.all().values_list("amount", flat=True))
|
||||||
purchases = sum(Order.objects.all().values_list('order_sum', flat=True))
|
purchases = sum(Order.objects.all().values_list("order_sum", flat=True))
|
||||||
saldo = deposits - purchases
|
saldo = deposits - purchases
|
||||||
|
|
||||||
deposits_start = sum(Transaction.objects.filter(created__lt=start_time).values_list('amount', flat=True))
|
deposits_start = sum(Transaction.objects.filter(created__lt=start_time).values_list("amount", flat=True))
|
||||||
purchases_start = sum(Order.objects.filter(created__lt=start_time).values_list('order_sum', flat=True))
|
purchases_start = sum(Order.objects.filter(created__lt=start_time).values_list("order_sum", flat=True))
|
||||||
saldo_start = deposits_start - purchases_start
|
saldo_start = deposits_start - purchases_start
|
||||||
|
|
||||||
deposits_end = sum(Transaction.objects.filter(created__lt=end_time).values_list('amount', flat=True))
|
deposits_end = sum(Transaction.objects.filter(created__lt=end_time).values_list("amount", flat=True))
|
||||||
purchases_end = sum(Order.objects.filter(created__lt=end_time).values_list('order_sum', flat=True))
|
purchases_end = sum(Order.objects.filter(created__lt=end_time).values_list("order_sum", flat=True))
|
||||||
saldo_end = deposits_end - purchases_end
|
saldo_end = deposits_end - purchases_end
|
||||||
|
|
||||||
deposits_diff = deposits_end - deposits_start
|
deposits_diff = deposits_end - deposits_start
|
||||||
purchases_diff = purchases_end - purchases_start
|
purchases_diff = purchases_end - purchases_start
|
||||||
saldo_diff = saldo_end - saldo_start
|
saldo_diff = saldo_end - saldo_start
|
||||||
|
|
||||||
return render(request, 'report.html', locals())
|
return render(request, "report.html", locals())
|
||||||
|
|
||||||
|
|
||||||
def inventory(request):
|
def inventory(request):
|
||||||
products = Product.objects.all().order_by('-active', 'inventory_amount', 'name')
|
products = Product.objects.all().order_by("-active", "inventory_amount", "name")
|
||||||
transactions = InventoryTransaction.objects.all().order_by('-created')
|
transactions = InventoryTransaction.objects.all().order_by("-created")
|
||||||
products_active = products.filter(active=True)
|
products_active = products.filter(active=True)
|
||||||
wholesale_value = sum([p.wholesale_value for p in products_active])
|
wholesale_value = sum([p.wholesale_value for p in products_active])
|
||||||
num_products = sum(products_active.values_list('inventory_amount', flat=True))
|
num_products = sum(products_active.values_list("inventory_amount", flat=True))
|
||||||
|
|
||||||
return render(request, 'inventory.html', locals())
|
return render(request, "inventory.html", locals())
|
||||||
|
|
||||||
|
|
||||||
def inventory_add(request):
|
def inventory_add(request):
|
||||||
@@ -125,12 +133,12 @@ def inventory_add(request):
|
|||||||
itrans.product.save()
|
itrans.product.save()
|
||||||
itrans.save()
|
itrans.save()
|
||||||
messages.success(request, "Satt inn {0} stk {1}.".format(itrans.amount, itrans.product))
|
messages.success(request, "Satt inn {0} stk {1}.".format(itrans.amount, itrans.product))
|
||||||
return redirect('inventory-add')
|
return redirect("inventory-add")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
messages.error(request, 'Feil med skjemaet, se under.')
|
messages.error(request, "Feil med skjemaet, se under.")
|
||||||
form = InventoryTransactionForm(data=request.POST)
|
form = InventoryTransactionForm(data=request.POST)
|
||||||
else:
|
else:
|
||||||
form = InventoryTransactionForm()
|
form = InventoryTransactionForm()
|
||||||
|
|
||||||
return render(request, 'inventory_add.html', locals())
|
return render(request, "inventory_add.html", locals())
|
||||||
+16
-23
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
@@ -13,13 +11,13 @@ from main.models import Product, OrderLine, Order
|
|||||||
|
|
||||||
def stats_list(request):
|
def stats_list(request):
|
||||||
products = Product.objects.filter(active=True)
|
products = Product.objects.filter(active=True)
|
||||||
order_groups = ['hourly', 'daily', 'monthly', 'yearly']
|
order_groups = ["hourly", "daily", "monthly", "yearly"]
|
||||||
|
|
||||||
return render(request, 'stats.html', {'products': products, 'order_groups': order_groups})
|
return render(request, "stats.html", {"products": products, "order_groups": order_groups})
|
||||||
|
|
||||||
|
|
||||||
class OrdersView(View):
|
class OrdersView(View):
|
||||||
grouping = 'orders'
|
grouping = "orders"
|
||||||
|
|
||||||
def get_range(self):
|
def get_range(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@@ -42,16 +40,16 @@ class OrdersView(View):
|
|||||||
|
|
||||||
orders_grouped = self.group_by(orders)
|
orders_grouped = self.group_by(orders)
|
||||||
data = {
|
data = {
|
||||||
'start': str(orders[0].created),
|
"start": str(orders[0].created),
|
||||||
'orders': [[k, v] for k, v in orders_grouped.items()],
|
"orders": [[k, v] for k, v in orders_grouped.items()],
|
||||||
'total': sum(orders_grouped.values()),
|
"total": sum(orders_grouped.values()),
|
||||||
'grouping': self.grouping
|
"grouping": self.grouping,
|
||||||
}
|
}
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
class OrdersHourlyView(OrdersView):
|
class OrdersHourlyView(OrdersView):
|
||||||
grouping = 'hourly'
|
grouping = "hourly"
|
||||||
|
|
||||||
def get_range(self):
|
def get_range(self):
|
||||||
return range(0, 24)
|
return range(0, 24)
|
||||||
@@ -61,7 +59,7 @@ class OrdersHourlyView(OrdersView):
|
|||||||
|
|
||||||
|
|
||||||
class OrdersDailyView(OrdersView):
|
class OrdersDailyView(OrdersView):
|
||||||
grouping = 'daily'
|
grouping = "daily"
|
||||||
|
|
||||||
def get_range(self):
|
def get_range(self):
|
||||||
return range(0, 7)
|
return range(0, 7)
|
||||||
@@ -71,7 +69,7 @@ class OrdersDailyView(OrdersView):
|
|||||||
|
|
||||||
|
|
||||||
class OrdersMonthlyView(OrdersView):
|
class OrdersMonthlyView(OrdersView):
|
||||||
grouping = 'monthly'
|
grouping = "monthly"
|
||||||
|
|
||||||
def get_range(self):
|
def get_range(self):
|
||||||
return range(1, 13)
|
return range(1, 13)
|
||||||
@@ -81,14 +79,14 @@ class OrdersMonthlyView(OrdersView):
|
|||||||
|
|
||||||
|
|
||||||
class OrdersYearlyView(OrdersView):
|
class OrdersYearlyView(OrdersView):
|
||||||
grouping = 'yearly'
|
grouping = "yearly"
|
||||||
|
|
||||||
def get_range(self):
|
def get_range(self):
|
||||||
first = Order.objects.order_by('created').first()
|
first = Order.objects.order_by("created").first()
|
||||||
last = timezone.now().year
|
last = timezone.now().year
|
||||||
first = first.created.year if first else last
|
first = first.created.year if first else last
|
||||||
|
|
||||||
return range(first, last+1)
|
return range(first, last + 1)
|
||||||
|
|
||||||
def get_key(self, order):
|
def get_key(self, order):
|
||||||
return order.created.year
|
return order.created.year
|
||||||
@@ -112,16 +110,14 @@ def stats_products_realtime(request):
|
|||||||
for product, value in products.items():
|
for product, value in products.items():
|
||||||
serialized_products[product.name] = value
|
serialized_products[product.name] = value
|
||||||
|
|
||||||
response = {
|
response = {"products": serialized_products}
|
||||||
'products': serialized_products
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonResponse(response)
|
return JsonResponse(response)
|
||||||
|
|
||||||
|
|
||||||
def stats_products_per_user(request, user_id=None):
|
def stats_products_per_user(request, user_id=None):
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return JsonResponse({'error': 'Missing param user_id'})
|
return JsonResponse({"error": "Missing param user_id"})
|
||||||
|
|
||||||
order_lines = OrderLine.objects.filter(order__customer__pk=user_id).order_by("order__created")
|
order_lines = OrderLine.objects.filter(order__customer__pk=user_id).order_by("order__created")
|
||||||
products = {}
|
products = {}
|
||||||
@@ -135,8 +131,5 @@ def stats_products_per_user(request, user_id=None):
|
|||||||
|
|
||||||
serialized_products = []
|
serialized_products = []
|
||||||
for product, value in products.items():
|
for product, value in products.items():
|
||||||
serialized_products.append({
|
serialized_products.append({"name": product.name, "data": value})
|
||||||
'name': product.name,
|
|
||||||
'data': value
|
|
||||||
})
|
|
||||||
return JsonResponse(serialized_products, safe=False)
|
return JsonResponse(serialized_products, safe=False)
|
||||||
+16
-17
@@ -13,40 +13,39 @@ def create_user(request):
|
|||||||
form = SimpleCreateUserForm(data=request.POST)
|
form = SimpleCreateUserForm(data=request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
user = form.save()
|
user = form.save()
|
||||||
messages.success(request, "Hurra! {} {} er lagt til".format(user.first_name, user.last_name))
|
messages.success(
|
||||||
return redirect('create-user')
|
request,
|
||||||
|
"Hurra! {} {} er lagt til".format(user.first_name, user.last_name),
|
||||||
|
)
|
||||||
|
return redirect("create-user")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
messages.error(request, 'Feil med skjemaet, se under.')
|
messages.error(request, "Feil med skjemaet, se under.")
|
||||||
form = SimpleCreateUserForm(data=request.POST)
|
form = SimpleCreateUserForm(data=request.POST)
|
||||||
else:
|
else:
|
||||||
form = SimpleCreateUserForm()
|
form = SimpleCreateUserForm()
|
||||||
|
|
||||||
return render(request, 'registration/create_user.html', {'form': form})
|
return render(request, "registration/create_user.html", {"form": form})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def profile(request):
|
def profile(request):
|
||||||
transactions = Transaction.objects.filter(user=request.user.pk)
|
transactions = Transaction.objects.filter(user=request.user.pk)
|
||||||
transaction_totals = {
|
transaction_totals = {"sum": sum([t.amount for t in transactions])}
|
||||||
'sum': sum([t.amount for t in transactions])
|
|
||||||
}
|
|
||||||
orders = Order.objects.filter(customer=request.user.pk)
|
orders = Order.objects.filter(customer=request.user.pk)
|
||||||
orders_totals = {
|
orders_totals = {"sum": sum([o.order_sum for o in orders])}
|
||||||
'sum': sum([o.order_sum for o in orders])
|
|
||||||
}
|
|
||||||
|
|
||||||
# order_lines = OrderLine.objects.filter(order__customer__pk=1).order_by("order__created")
|
# order_lines = OrderLine.objects.filter(order__customer__pk=1).order_by("order__created")
|
||||||
order_report = _orders_monthly(filter_by={'customer': request.user.pk})
|
order_report = _orders_monthly(filter_by={"customer": request.user.pk})
|
||||||
top_months = order_report.order_by('-sum')[:5]
|
top_months = order_report.order_by("-sum")[:5]
|
||||||
|
|
||||||
return render(request, 'profile.html', locals())
|
return render(request, "profile.html", locals())
|
||||||
|
|
||||||
|
|
||||||
def _orders_monthly(filter_by={}, order_by='month'):
|
def _orders_monthly(filter_by={}, order_by="month"):
|
||||||
|
|
||||||
truncate_date = connection.ops.date_trunc_sql('month', 'created')
|
truncate_date = connection.ops.date_trunc_sql("month", "created")
|
||||||
qs = Order.objects.filter(**filter_by).extra({'month': truncate_date})
|
qs = Order.objects.filter(**filter_by).extra({"month": truncate_date})
|
||||||
order_report = qs.values('month').annotate(sum=Sum('order_sum'), num=Count('pk')).order_by(order_by)
|
order_report = qs.values("month").annotate(sum=Sum("order_sum"), num=Count("pk")).order_by(order_by)
|
||||||
|
|
||||||
return order_report
|
return order_report
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[tool.black]
|
||||||
|
line-length = 120
|
||||||
|
target_version = ['py39']
|
||||||
Reference in new issue
Block a user