diff --git a/backup.sh b/backup.sh new file mode 100644 index 0000000..c8cada6 --- /dev/null +++ b/backup.sh @@ -0,0 +1,3 @@ +date=$(date +%Y-%m-%d"_"%H_%M_%S) +docker-compose exec web python3 manage.py dumpdata --natural-foreign --exclude auth.permission --exclude contenttypes --indent 1 | gzip > backups/djangodump_${date}.json.gz +docker-compose exec db pg_dump -U partdoc partdoc | gzip > backups/pgdump_${date}.sql.gz diff --git a/docker-compose.yml b/docker-compose.yml index bd96ac5..42c885a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: working_dir: /app command: python3 ./manage.py runserver 0.0.0.0:8000 ports: - - 8080:8000 + - 8081:8000 depends_on: - db db: diff --git a/partdoc/Dockerfile b/partdoc/Dockerfile index 6c63e63..664fa89 100644 --- a/partdoc/Dockerfile +++ b/partdoc/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.10 +FROM alpine:3.11 ADD requirements.txt / RUN apk add --update --no-cache python3 py3-psycopg2 && \ diff --git a/partdoc/parts/admin.py b/partdoc/parts/admin.py index 0b7d4e4..362a605 100644 --- a/partdoc/parts/admin.py +++ b/partdoc/parts/admin.py @@ -1,9 +1,9 @@ from django.contrib import admin -from .models import Brand, Product, Version, Sketch, Part, Usage, BrandedPart, ProductUsage +from .models import Brand, Product, Version, Sketch, Part, Usage, BrandedPart, ProductUsage, Cart, CartEntry, Shop # admin.site.register([Brand, Product, Version, Sketch, Part, Usage, BrandedPart, ProductUsage]) -admin.site.register([Brand, Product, Version, Sketch]) +admin.site.register([Brand, Product, Version, Sketch, Shop, CartEntry]) class BrandedPartInline(admin.StackedInline): @@ -51,7 +51,18 @@ class BrandedPartAdmin(admin.ModelAdmin): autocomplete_fields = ["parts"] +class CartEntryInline(admin.StackedInline): + model = CartEntry + extra = 1 + raw_id_fields = ("origins",) + +class CartAdmin(admin.ModelAdmin): + inlines = [CartEntryInline] + #search_fields = ["number"] + #autocomplete_fields = ["parts"] + admin.site.register(Usage, UsageAdmin) admin.site.register(ProductUsage, ProductUsageAdmin) admin.site.register(Part, PartAdmin) admin.site.register(BrandedPart, BrandedPartAdmin) +admin.site.register(Cart, CartAdmin) diff --git a/partdoc/parts/models.py b/partdoc/parts/models.py index fd51915..d5667bc 100644 --- a/partdoc/parts/models.py +++ b/partdoc/parts/models.py @@ -1,5 +1,5 @@ from django.db import models, transaction - +from django.db.models import Sum class PartModel(models.Model): creation = models.DateTimeField(auto_now_add=True) @@ -157,3 +157,49 @@ class ProductUsage(PartModel): def __str__(self): no = self.usage.part.number if self.usage else "--(part.number)--" return "ProductUsage: " + self.product.name + " @ " + str(self.quantity) + "; " + str(no) + + + +class Shop(PartModel): + name = models.CharField(max_length=512) + url = models.URLField(null=True, blank=True) + + def __str__(self): + return self.name + +class Cart(PartModel): + name = models.CharField(max_length=512) + + def __str__(self): + return self.name + + def add(self, product_usage): + part = product_usage.usage.part + entry = None + for i in self.entries.all(): + if part == i.part: + entry = i + break + if not entry: + entry = CartEntry.objects.create(cart=self) + entry.origins.add(product_usage) + entry.save() + + +class CartEntry(PartModel): + cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name="entries") + origins = models.ManyToManyField(ProductUsage) + shop = models.ForeignKey(Shop, on_delete=models.CASCADE, null=True, blank=True) + in_cart = models.BooleanField(default=False, blank=True) + ordered = models.BooleanField(default=False, blank=True) + + @property + def part(self): + return self.origins.first().usage.part + + @property + def quantity(self): + return self.origins.aggregate(sum=Sum('quantity'))["sum"] + + def get_quantity(self): + return self.quantity diff --git a/partdoc/parts/static/parts/update_cart.js b/partdoc/parts/static/parts/update_cart.js new file mode 100644 index 0000000..648c069 --- /dev/null +++ b/partdoc/parts/static/parts/update_cart.js @@ -0,0 +1,47 @@ +function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie !== '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = cookies[i].trim(); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; +} + +function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); +} +$.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && !this.crossDomain) { +var csrftoken = getCookie('csrftoken'); + xhr.setRequestHeader("X-CSRFToken", csrftoken); + xhr.setRequestHeader("X-Frame-Options", "SAMEORIGIN"); + } + } +}); + +function update_cart(pu_id){ + var cart = $("#cart")[0].value; + console.log("cart", cart); + $.ajax({ + type: "POST", + url: "/parts/cart/add/", + data: { + "cart_name": cart, + "product_usage_id": pu_id + }, + success: function(data){ + console.log(data); + } + }).done(function(msg){console.log("done" + msg);console.log(msg)}) + .fail(function(msg){console.log("fail" + msg);console.log(msg)}) + ; +} diff --git a/partdoc/parts/templates/parts/cart.html b/partdoc/parts/templates/parts/cart.html new file mode 100644 index 0000000..bb1e06f --- /dev/null +++ b/partdoc/parts/templates/parts/cart.html @@ -0,0 +1,14 @@ +
| Produkt | diff --git a/partdoc/parts/templates/parts/products.html b/partdoc/parts/templates/parts/products.html index 9ce0ba9..a8d5b35 100644 --- a/partdoc/parts/templates/parts/products.html +++ b/partdoc/parts/templates/parts/products.html @@ -4,3 +4,11 @@
|---|