diff --git a/.gitignore b/.gitignore index bce8157..1fbae0d 100644 --- a/.gitignore +++ b/.gitignore @@ -276,3 +276,5 @@ mailpit wooster_django/media/ .pytest_cache/ + +db.sqlite3 diff --git a/config/settings/base.py b/config/settings/base.py index aeed158..e380b0c 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -3,22 +3,25 @@ """ from pathlib import Path -import environ +import environ # type:ignore BASE_DIR = Path(__file__).resolve(strict=True).parent.parent.parent # wooster_django/ APPS_DIR = BASE_DIR / "wooster_django" env = environ.Env() +print(f"'APPS_DIR': {APPS_DIR}") READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=False) if READ_DOT_ENV_FILE: # OS environment variables take precedence over variables from .env env.read_env(str(BASE_DIR / ".env")) + print(f"'BASE_DIR': {BASE_DIR}") # GENERAL # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#debug DEBUG = env.bool("DJANGO_DEBUG", False) +print(f"DEBUG: {DEBUG}") # Local time zone. Choices are # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # though not all of them may be available with every OS. @@ -52,6 +55,14 @@ default="postgres://localhost/wooster_django", ), } + +# DATABASES = { +# "default": { +# "ENGINE": "django.db.backends.sqlite3", +# "NAME": BASE_DIR / "db.sqlite3", +# } +# } + DATABASES["default"]["ATOMIC_REQUESTS"] = True # https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-DEFAULT_AUTO_FIELD DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" @@ -83,17 +94,18 @@ "allauth", "allauth.account", "allauth.socialaccount", - "wooster_django.basemodels", ] LOCAL_APPS = [ "wooster_django.users", # Your stuff: custom apps go here # "wooster_django.containers", + "wooster_django.basemodels", "wooster_django.customers", "wooster_django.inventory", "wooster_django.orders", "wooster_django.projects", + "wooster_django.receipts", ] # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS diff --git a/config/settings/local.py b/config/settings/local.py index 4d6796c..c0894fd 100644 --- a/config/settings/local.py +++ b/config/settings/local.py @@ -58,3 +58,5 @@ # Your stuff... # ------------------------------------------------------------------------------ +# print(f"'DATABASES': {DATABASES}") +# print(f"'INSTALLED_APPS': {INSTALLED_APPS}") diff --git a/config/urls.py b/config/urls.py index 3067d65..e1abaee 100644 --- a/config/urls.py +++ b/config/urls.py @@ -14,6 +14,7 @@ path("users/", include("wooster_django.users.urls", namespace="users")), path("accounts/", include("allauth.urls")), # Your stuff: custom urls includes go here + # path("receipts/", include("wooster_django.receipts.urls", namespace="receipts")), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/manage.py b/manage.py index fcb7ee8..f5bd5d5 100644 --- a/manage.py +++ b/manage.py @@ -14,18 +14,19 @@ # exceptions on Python 2. try: import django # noqa - except ImportError: + except ImportError as e: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" - ) + ) from e raise # This allows easy placement of apps within the interior # wooster_django directory. current_path = Path(__file__).parent.resolve() + print(f"'current_path': {current_path}") sys.path.append(str(current_path / "wooster_django")) execute_from_command_line(sys.argv) diff --git a/pyproject.toml b/pyproject.toml index cfcd04d..a14fae8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,11 +12,18 @@ plugins = ["django_coverage_plugin"] # ==== Ruff ==== [tool.ruff] +fix = true line-length = 119 select = ["E", "F", "B"] unfixable = ["B"] target-version = "py311" +[tool.ruff.format] +quote-style = "double" + +[tool.ruff.lint] +fixable = ["ALL"] + # ==== black ==== [tool.black] @@ -43,6 +50,9 @@ warn_redundant_casts = true warn_unused_configs = true plugins = ["mypy_django_plugin.main"] +[tool.mypy_django_plugin] +ignore_missing_model_attributes = true + [[tool.mypy.overrides]] # Django migrations should not produce any errors: module = "*.migrations.*" diff --git a/requirements/local.txt b/requirements/local.txt index 3ea392a..7b108a1 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -10,6 +10,7 @@ psycopg[binary]==3.1.14 # https://github.com/psycopg/psycopg django-stubs[compatible-mypy]==4.2.6 # https://github.com/typeddjango/django-stubs pytest==7.4.3 # https://github.com/pytest-dev/pytest pytest-sugar==0.9.7 # https://github.com/Frozenball/pytest-sugar +types-python-slugify==8.0.2.20240127 # Documentation # ------------------------------------------------------------------------------ @@ -22,7 +23,7 @@ sphinx-autobuild==2021.3.14 # https://github.com/GaretJax/sphinx-autobuild # flake8-isort==6.1.1 # https://github.com/gforcada/flake8-isort ruff==0.1.6 coverage==7.3.2 # https://github.com/nedbat/coveragepy -black==23.11.0 # https://github.com/psf/black +# black==23.11.0 # https://github.com/psf/black djlint==1.34.0 # https://github.com/Riverside-Healthcare/djLint pylint-django==2.5.5 # https://github.com/PyCQA/pylint-django pre-commit==3.5.0 # https://github.com/pre-commit/pre-commit diff --git a/wooster_django/basemodels/migrations/0002_delete_documentnumber.py b/wooster_django/basemodels/migrations/0002_delete_documentnumber.py new file mode 100644 index 0000000..d8a6f87 --- /dev/null +++ b/wooster_django/basemodels/migrations/0002_delete_documentnumber.py @@ -0,0 +1,15 @@ +# Generated by Django 4.2.7 on 2024-02-12 08:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("basemodels", "0001_initial"), + ] + + operations = [ + migrations.DeleteModel( + name="DocumentNumber", + ), + ] diff --git a/wooster_django/basemodels/models/__init__.py b/wooster_django/basemodels/models/__init__.py new file mode 100644 index 0000000..c0b7b34 --- /dev/null +++ b/wooster_django/basemodels/models/__init__.py @@ -0,0 +1,5 @@ +from .abstract import BaseModel + +# from .shared import WhyDoesThisError + +__all__ = ["BaseModel"] diff --git a/wooster_django/basemodels/models.py b/wooster_django/basemodels/models/abstract.py similarity index 53% rename from wooster_django/basemodels/models.py rename to wooster_django/basemodels/models/abstract.py index af65b14..4281ebe 100644 --- a/wooster_django/basemodels/models.py +++ b/wooster_django/basemodels/models/abstract.py @@ -24,25 +24,3 @@ def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.name) super().save(*args, **kwargs) - - -class DocumentNumber(models.Model): - document = models.CharField(max_length=50, primary_key=True, unique=True) - prefix = models.CharField(max_length=10, blank=True, null=True) - padding_digits = models.IntegerField(blank=True, null=True) - next_counter = models.IntegerField(default=1) - last_number = models.CharField(max_length=50, editable=False, null=True) - last_generated_date = models.DateTimeField(auto_now=True) - - def get_next_number(self): - prefix = self.prefix - next_counter = self.next_counter - padded_counter = str(next_counter).zfill(self.padding_digits) - number = f"{prefix}{padded_counter}" - - self.next_counter += 1 - self.last_number = number - - self.save() - - return number diff --git a/wooster_django/basemodels/models/shared.py b/wooster_django/basemodels/models/shared.py new file mode 100644 index 0000000..f77650e --- /dev/null +++ b/wooster_django/basemodels/models/shared.py @@ -0,0 +1,24 @@ +from django.db import models + + +# Create your models here. +class WhyDoesThisError(models.Model): + document = models.CharField(max_length=50, primary_key=True, unique=True) + prefix = models.CharField(max_length=10, blank=True, null=True) + padding_digits = models.IntegerField(blank=True, null=True) + next_counter = models.IntegerField(default=1) + last_number = models.CharField(max_length=50, editable=False, null=True) + last_generated_date = models.DateTimeField(auto_now=True) + + def get_next_number(self): + prefix = self.prefix + next_counter = self.next_counter + padded_counter = str(next_counter).zfill(self.padding_digits) + number = f"{prefix}{padded_counter}" + + self.next_counter += 1 + self.last_number = number + + self.save() + + return number diff --git a/wooster_django/customers/models.py b/wooster_django/customers/models.py index 6fe9527..c752c01 100644 --- a/wooster_django/customers/models.py +++ b/wooster_django/customers/models.py @@ -1,7 +1,7 @@ # from django.conf import settings from django.db import models from django.utils.translation import gettext_lazy as _ -from phonenumber_field.modelfields import PhoneNumberField +from phonenumber_field.modelfields import PhoneNumberField # type: ignore # from slugify import slugify # from wooster_django.basemodels.models import BaseModel diff --git a/wooster_django/inventory/models.py b/wooster_django/inventory/models.py index 69e8ce9..fa733af 100644 --- a/wooster_django/inventory/models.py +++ b/wooster_django/inventory/models.py @@ -1,12 +1,14 @@ import shortuuid from basemodels.models import BaseModel -from colorfield.fields import ColorField +from colorfield.fields import ColorField # type:ignore # from django.conf import settings from django.db import models from django.utils.translation import gettext_lazy as _ from slugify import slugify +# from wooster_django.projects.models import BasalModel + # Chosen from https://htmlcolorcodes.com/color-names COMMON_COLOR_PALETTE = [ ("#FFFFFF", "white"), diff --git a/wooster_django/orders/migrations/0007_documentnumbers.py b/wooster_django/orders/migrations/0007_documentnumbers.py new file mode 100644 index 0000000..56445b1 --- /dev/null +++ b/wooster_django/orders/migrations/0007_documentnumbers.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.7 on 2024-02-12 08:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("orders", "0006_order_notes_orderitem_notes_alter_orderitem_rank"), + ] + + operations = [ + migrations.CreateModel( + name="DocumentNumbers", + fields=[ + ("document", models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)), + ("prefix", models.CharField(blank=True, max_length=10, null=True)), + ("padding_digits", models.IntegerField(blank=True, null=True)), + ("next_counter", models.IntegerField(default=1)), + ("last_number", models.CharField(editable=False, max_length=50, null=True)), + ("last_generated_date", models.DateTimeField(auto_now=True)), + ], + ), + ] diff --git a/wooster_django/orders/migrations/0008_rename_documentnumbers_documentnumber.py b/wooster_django/orders/migrations/0008_rename_documentnumbers_documentnumber.py new file mode 100644 index 0000000..8e00691 --- /dev/null +++ b/wooster_django/orders/migrations/0008_rename_documentnumbers_documentnumber.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.7 on 2024-02-12 09:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("orders", "0007_documentnumbers"), + ] + + operations = [ + migrations.RenameModel( + old_name="DocumentNumbers", + new_name="DocumentNumber", + ), + ] diff --git a/wooster_django/orders/models.py b/wooster_django/orders/models.py index cff58f7..5e2fc7b 100644 --- a/wooster_django/orders/models.py +++ b/wooster_django/orders/models.py @@ -2,7 +2,7 @@ from datetime import datetime as dt from datetime import timedelta -from basemodels.models import BaseModel, DocumentNumber +from basemodels.models import BaseModel # , WhyDoesThisError from django.db import models from django.db.models import F, Max, Sum from django.utils.translation import gettext_lazy as _ @@ -12,10 +12,34 @@ # from wooster_django.projects.models import Project from slugify import slugify -# from wooster_django.projects.models import DocumentNumber +# from wooster_django.projects.models import DocumentNumbers # , BasalModel # Create your models here. + + +class DocumentNumber(models.Model): + document = models.CharField(max_length=50, primary_key=True, unique=True) + prefix = models.CharField(max_length=10, blank=True, null=True) + padding_digits = models.IntegerField(blank=True, null=True) + next_counter = models.IntegerField(default=1) + last_number = models.CharField(max_length=50, editable=False, null=True) + last_generated_date = models.DateTimeField(auto_now=True) + + def get_next_number(self): + prefix = self.prefix + next_counter = self.next_counter + padded_counter = str(next_counter).zfill(self.padding_digits) + number = f"{prefix}{padded_counter}" + + self.next_counter += 1 + self.last_number = number + + self.save() + + return number + + class Order(BaseModel): name = None number = models.CharField(_("Order Number"), max_length=50, unique=True, editable=False) diff --git a/wooster_django/projects/admin.py b/wooster_django/projects/admin.py index b2a6027..d9b06db 100644 --- a/wooster_django/projects/admin.py +++ b/wooster_django/projects/admin.py @@ -1,12 +1,11 @@ from django.contrib import admin -from .models import DocumentNumber, Project - +from .models import Project # Register your models here. -@admin.register(DocumentNumber) -class DocumentNumberAdmin(admin.ModelAdmin): - list_display = ["document", "last_number", "next_counter"] +# @admin.register(DocumentNumbers) +# class DocumentNumberAdmin(admin.ModelAdmin): +# list_display = ["document", "last_number", "next_counter"] @admin.register(Project) diff --git a/wooster_django/projects/migrations/0004_rename_documentnumber_documentnumbers.py b/wooster_django/projects/migrations/0004_rename_documentnumber_documentnumbers.py new file mode 100644 index 0000000..500f5fe --- /dev/null +++ b/wooster_django/projects/migrations/0004_rename_documentnumber_documentnumbers.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.7 on 2024-02-12 08:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("projects", "0003_project_order"), + ] + + operations = [ + migrations.RenameModel( + old_name="DocumentNumber", + new_name="DocumentNumbers", + ), + ] diff --git a/wooster_django/projects/migrations/0005_rename_documentnumbers_documentnumber.py b/wooster_django/projects/migrations/0005_rename_documentnumbers_documentnumber.py new file mode 100644 index 0000000..55738b4 --- /dev/null +++ b/wooster_django/projects/migrations/0005_rename_documentnumbers_documentnumber.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.7 on 2024-02-12 09:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("projects", "0004_rename_documentnumber_documentnumbers"), + ] + + operations = [ + migrations.RenameModel( + old_name="DocumentNumbers", + new_name="DocumentNumber", + ), + ] diff --git a/wooster_django/projects/models.py b/wooster_django/projects/models.py index c8bb9dd..78faf24 100644 --- a/wooster_django/projects/models.py +++ b/wooster_django/projects/models.py @@ -1,29 +1,29 @@ -from django.conf import settings +# from django.conf import settings +from basemodels.models import BaseModel # , WhyDoesThisError from django.db import models from django.utils.translation import gettext_lazy as _ from slugify import slugify - # Create your models here. -class BaseModel(models.Model): - """Base model for standardization. Includes generating slug.""" +# class BasalModel(models.Model): +# """Base model for standardization. Includes generating slug.""" - name = models.CharField(max_length=50) - created_by = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("created by"), on_delete=models.PROTECT) - slug = models.SlugField(unique=True, editable=False) - created_date = models.DateTimeField(_("created date"), auto_now_add=True) - modified_date = models.DateTimeField(_("modified date"), auto_now=True) +# name = models.CharField(max_length=50) +# created_by = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("created by"), on_delete=models.PROTECT) +# slug = models.SlugField(unique=True, editable=False) +# created_date = models.DateTimeField(_("created date"), auto_now_add=True) +# modified_date = models.DateTimeField(_("modified date"), auto_now=True) - class Meta: - abstract = True +# class Meta: +# abstract = True - def __str__(self): - return self.name +# def __str__(self): +# return self.name - def save(self, *args, **kwargs): - if not self.slug: - self.slug = slugify(self.name) - super().save(*args, **kwargs) +# def save(self, *args, **kwargs): +# if not self.slug: +# self.slug = slugify(self.name) +# super().save(*args, **kwargs) class DocumentNumber(models.Model): diff --git a/wooster_django/receipts/__init__.py b/wooster_django/receipts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wooster_django/receipts/admin.py b/wooster_django/receipts/admin.py new file mode 100644 index 0000000..846f6b4 --- /dev/null +++ b/wooster_django/receipts/admin.py @@ -0,0 +1 @@ +# Register your models here. diff --git a/wooster_django/receipts/apps.py b/wooster_django/receipts/apps.py new file mode 100644 index 0000000..79a3f82 --- /dev/null +++ b/wooster_django/receipts/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ReceiptsConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "receipts" diff --git a/wooster_django/receipts/migrations/0001_initial.py b/wooster_django/receipts/migrations/0001_initial.py new file mode 100644 index 0000000..3a31d8a --- /dev/null +++ b/wooster_django/receipts/migrations/0001_initial.py @@ -0,0 +1,87 @@ +# Generated by Django 4.2.7 on 2024-02-12 09:46 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Merchant", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("name", models.CharField(max_length=50)), + ("slug", models.SlugField(editable=False, unique=True)), + ("created_date", models.DateTimeField(auto_now_add=True, verbose_name="created date")), + ("modified_date", models.DateTimeField(auto_now=True, verbose_name="modified date")), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + verbose_name="created by", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="Receipt", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("slug", models.UUIDField(auto_created=True, verbose_name="slug")), + ("created_date", models.DateTimeField(auto_now_add=True, verbose_name="created date")), + ("modified_date", models.DateTimeField(auto_now=True, verbose_name="modified date")), + ("transaction_date", models.DateField(verbose_name="Transaction Date")), + ("receipt_file", models.FileField(upload_to="uploads", verbose_name="Receipt")), + ("analyze_result", models.JSONField(verbose_name="Analyze Result")), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + verbose_name="created by", + ), + ), + ( + "merchant", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="receipts.merchant", verbose_name="Merchant" + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="ReceiptItem", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("product_code", models.CharField(max_length=50, verbose_name="Product Code")), + ("description", models.CharField(max_length=100, verbose_name="Description")), + ( + "quantity", + models.DecimalField(decimal_places=2, default=1.0, max_digits=5, verbose_name="Quantity"), + ), + ("total_price", models.DecimalField(decimal_places=2, max_digits=5, verbose_name="Total Price")), + ("tax_code", models.CharField(blank=True, max_length=5, verbose_name="Tax Code")), + ("taxable_status", models.BooleanField(verbose_name="Taxable Status")), + ( + "receipt", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="receipts.receipt", verbose_name="Receipt" + ), + ), + ], + ), + ] diff --git a/wooster_django/receipts/migrations/__init__.py b/wooster_django/receipts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wooster_django/receipts/models.py b/wooster_django/receipts/models.py new file mode 100644 index 0000000..f00a406 --- /dev/null +++ b/wooster_django/receipts/models.py @@ -0,0 +1,49 @@ +from django.conf import settings +from django.db import models +from django.utils.translation import gettext_lazy as _ +from slugify import slugify + + +# Create your models here. +class ReceiptBaseModel(models.Model): + """Base model for standardization. Includes generating slug.""" + + name = models.CharField(max_length=50) + created_by = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("created by"), on_delete=models.PROTECT) + slug = models.SlugField(unique=True, editable=False) + created_date = models.DateTimeField(_("created date"), auto_now_add=True) + modified_date = models.DateTimeField(_("modified date"), auto_now=True) + + class Meta: + abstract = True + + def __str__(self): + return self.name + + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.name) + super().save(*args, **kwargs) + + +class Merchant(ReceiptBaseModel): + pass + + +class Receipt(ReceiptBaseModel): + name = None # type:ignore + merchant = models.ForeignKey(Merchant, verbose_name=_("Merchant"), on_delete=models.PROTECT) + transaction_date = models.DateField(_("Transaction Date"), auto_now=False, auto_now_add=False) + receipt_file = models.FileField(_("Receipt"), upload_to="uploads", max_length=100) + analyze_result = models.JSONField(_("Analyze Result")) + slug = models.UUIDField(_("slug"), auto_created=True) + + +class ReceiptItem(models.Model): + product_code = models.CharField(_("Product Code"), max_length=50) + description = models.CharField(_("Description"), max_length=100) + quantity = models.DecimalField(_("Quantity"), max_digits=5, decimal_places=2, default=1.0) + total_price = models.DecimalField(_("Total Price"), max_digits=5, decimal_places=2) + tax_code = models.CharField(_("Tax Code"), max_length=5, blank=True) + taxable_status = models.BooleanField(_("Taxable Status")) + receipt: models.ForeignKey = models.ForeignKey(Receipt, verbose_name=_("Receipt"), on_delete=models.CASCADE) diff --git a/wooster_django/receipts/tests.py b/wooster_django/receipts/tests.py new file mode 100644 index 0000000..a39b155 --- /dev/null +++ b/wooster_django/receipts/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/wooster_django/receipts/urls.py b/wooster_django/receipts/urls.py new file mode 100644 index 0000000..78de5d0 --- /dev/null +++ b/wooster_django/receipts/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from .views import CreateReceiptView + +app_name = "receipts" +# fmt: off +urlpatterns = [ + path("create-receipt/", CreateReceiptView.as_view(), name="create_receipt") +] diff --git a/wooster_django/receipts/views.py b/wooster_django/receipts/views.py new file mode 100644 index 0000000..2a00f1d --- /dev/null +++ b/wooster_django/receipts/views.py @@ -0,0 +1,21 @@ +from django.contrib.auth import get_user_model + +# from django.utils.translation import gettext_lazy as _ +from django.views.generic import CreateView + +from .models import Receipt + +User = get_user_model() + + +# Create your views here. +class BaseCreateView(CreateView): + class Meta: + abstract = True + + pass + + +class CreateReceiptView(BaseCreateView): + model = Receipt + fields = ["receipt_file"] diff --git a/wooster_django/templates/receipts/receipt_form.html b/wooster_django/templates/receipts/receipt_form.html new file mode 100644 index 0000000..e920e3c --- /dev/null +++ b/wooster_django/templates/receipts/receipt_form.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} + +{% block title %} + Create/Upload Receipt +{% endblock +title %} +{% block content %} +
+{% endblock content %}