master
agp8x 2020-04-01 22:49:31 +02:00
parent f0cf85f3fa
commit 998a530500
14 changed files with 187 additions and 12 deletions

3
backup.sh Normal file
View File

@ -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

View File

@ -11,7 +11,7 @@ services:
working_dir: /app working_dir: /app
command: python3 ./manage.py runserver 0.0.0.0:8000 command: python3 ./manage.py runserver 0.0.0.0:8000
ports: ports:
- 8080:8000 - 8081:8000
depends_on: depends_on:
- db - db
db: db:

View File

@ -1,4 +1,4 @@
FROM alpine:3.10 FROM alpine:3.11
ADD requirements.txt / ADD requirements.txt /
RUN apk add --update --no-cache python3 py3-psycopg2 && \ RUN apk add --update --no-cache python3 py3-psycopg2 && \

View File

@ -1,9 +1,9 @@
from django.contrib import admin 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, Part, Usage, BrandedPart, ProductUsage])
admin.site.register([Brand, Product, Version, Sketch]) admin.site.register([Brand, Product, Version, Sketch, Shop, CartEntry])
class BrandedPartInline(admin.StackedInline): class BrandedPartInline(admin.StackedInline):
@ -51,7 +51,18 @@ class BrandedPartAdmin(admin.ModelAdmin):
autocomplete_fields = ["parts"] 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(Usage, UsageAdmin)
admin.site.register(ProductUsage, ProductUsageAdmin) admin.site.register(ProductUsage, ProductUsageAdmin)
admin.site.register(Part, PartAdmin) admin.site.register(Part, PartAdmin)
admin.site.register(BrandedPart, BrandedPartAdmin) admin.site.register(BrandedPart, BrandedPartAdmin)
admin.site.register(Cart, CartAdmin)

View File

@ -1,5 +1,5 @@
from django.db import models, transaction from django.db import models, transaction
from django.db.models import Sum
class PartModel(models.Model): class PartModel(models.Model):
creation = models.DateTimeField(auto_now_add=True) creation = models.DateTimeField(auto_now_add=True)
@ -157,3 +157,49 @@ class ProductUsage(PartModel):
def __str__(self): def __str__(self):
no = self.usage.part.number if self.usage else "--(part.number)--" no = self.usage.part.number if self.usage else "--(part.number)--"
return "ProductUsage: " + self.product.name + " @ " + str(self.quantity) + "; " + str(no) 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

View File

@ -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)})
;
}

View File

@ -0,0 +1,14 @@
<h1>{{cart.name}}</h1>
<h3>entries</h3>
<ul>
{% for entry in entries %}
<li>{{entry.quantity}} -- <a href="{% url 'parts:cart_entry' cart.id entry.id %}">{{entry.part}}</a>
<ul>
{% for pu in entry.origins.all %}
<li><a href="{% url 'parts:sketch' pu.usage.sketch.id %}">{{pu.usage.sketch.name}}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</ul>

View File

@ -0,0 +1,6 @@
<h1>{{cart.name}}</h1>
<h3>entries</h3>
TODO
<a href="{% url 'parts:cart' cart.id %}">{{cart.name}}</a>

View File

@ -1,5 +1,7 @@
<h1>part {{part.number}}</h1> <h1>part {{part.number}}</h1>
<h3>{{part.name}}</h3> <h3>{{part.get_name}}</h3>
<h4>{{full_name}}</h4>
<table> <table>
<tr> <tr>
<th>Produkt</th> <th>Produkt</th>

View File

@ -4,3 +4,11 @@
<li><a href="{% url 'parts:product' product.id %}">{{product.name}}</a></li> <li><a href="{% url 'parts:product' product.id %}">{{product.name}}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
<h1>Available carts</h1>
<ul>
{% for cart in cart_list %}
<li><a href="{% url 'parts:cart' cart.id %}">{{cart.name}}</a></li>
{% endfor %}
</ul>

View File

@ -1,13 +1,19 @@
{% load static %}
<script type="text/javascript" src="{% static 'parts/jquery-3.2.1.min.js' %}"></script>
<script type="text/javascript" src="{% static 'parts/update_cart.js' %}"></script>
<h1>{{sketch.name}}</h1> <h1>{{sketch.name}}</h1>
<a href="{{sketch.image.url}}"><img src="{{sketch.image.url}}" /></a> {% if sketch.image %}<a href="{{sketch.image.url}}"><img src="{{sketch.image.url}}" /></a> {% endif %}
<h2>{% for brand in sketch.get_brand %}{{brand.name}},{% endfor%} - {% for product in sketch.get_product %}{{product.name}},{% endfor%}</h2> <h2>{% for brand in sketch.get_brand %}{{brand.name}},{% endfor%} - {% for product in sketch.get_product %}{{product.name}},{% endfor%}</h2>
<ul> <ul>
{% for usage in sketch.usage_set.all %} {% for usage in sketch.usage_set.all %}
<li><a href="/parts/usage/{{usage.id}}/">{{usage}}</a></li> <!--<li><a href="/parts/usage/{{usage.id}}/">{{usage}}</a></li>-->
<li><a href="% url 'parts:usage' usage.id %">{usage}</a></li><!--TODO!--> <li><button type="button" onclick="update_cart({{usage.id}})" id="update_cart_{{usage.id}}" >+</button><a href="{% url 'parts:part' usage.part.id %}">{{usage}}</a></li><!--TODO!-->
{% endfor %} {% endfor %}
</ul> </ul>
<p>{{sketch.get_brand}}</p> <p>{{sketch.get_brand}}</p>
<p>{{sketch.get_brand.name.foo}}</p> <p>{{sketch.get_brand.name.foo}}</p>
<label for="cart_name">cart:</label><input name="cart_name" id="cart"></input>
<a href="{% url "parts:continue_sketch" sketch.product_id sketch.id %}">Edit</a> <a href="{% url "parts:continue_sketch" sketch.product_id sketch.id %}">Edit</a>

View File

@ -13,4 +13,7 @@ urlpatterns = [
path('add/<int:product_id>/sketch/<int:sketch_id>', views.sketch_add, name="continue_sketch"), path('add/<int:product_id>/sketch/<int:sketch_id>', views.sketch_add, name="continue_sketch"),
path('add/<int:product_id>/sketch', views.sketch_add, name="new_sketch"), path('add/<int:product_id>/sketch', views.sketch_add, name="new_sketch"),
path('search/part/', views.part_search, name="part_search"), path('search/part/', views.part_search, name="part_search"),
path('cart/add/', views.update_cart, name="cart_add"),
path('cart/<int:cart_id>/', views.cart, name="cart"),
path('cart/<int:cart_id>/entry/<int:entry_id>/', views.cart_entry, name="cart_entry"),
] ]

View File

@ -13,7 +13,10 @@ ADD_SKETCH_REQUIRES = ['sketch_name', 'sketch_number', '']
def index(request): def index(request):
product_list = Product.objects.all() product_list = Product.objects.all()
context = {'product_list': product_list} context = {
'product_list': product_list,
'cart_list': Cart.objects.all()
}
return render(request, 'parts/products.html', context) return render(request, 'parts/products.html', context)
@ -31,7 +34,8 @@ def sketch(request, sketch_id):
def __part_view(request, part): def __part_view(request, part):
sketches = part.usage_set.values('sketch', 'sketch_number', 'sketch__name', 'sketch__product__name', 'sketch__product__id').annotate(qty=Sum('productusage__quantity')) sketches = part.usage_set.values('sketch', 'sketch_number', 'sketch__name', 'sketch__product__name', 'sketch__product__id').annotate(qty=Sum('productusage__quantity'))
context = {'part': part, 'sketches': sketches} full_name = part.get_name(full=True)
context = {'part': part, 'sketches': sketches, 'full_name': full_name}
return render(request, 'parts/part.html', context) return render(request, 'parts/part.html', context)
def part(request, part_id): def part(request, part_id):
@ -179,3 +183,28 @@ def get_quantity(qnty_str):
def is_exact_sketchnumber(sketch_number): def is_exact_sketchnumber(sketch_number):
approximate = ("ähn", "vgl", "~", "zu") approximate = ("ähn", "vgl", "~", "zu")
return not any([i in sketch_number for i in approximate]) return not any([i in sketch_number for i in approximate])
def update_cart(request):
if request.method == "POST":
keys = ["cart_name", "product_usage_id"]
if all([key in request.POST for key in keys]):
cart, _ = Cart.objects.get_or_create(name=request.POST["cart_name"])
pu = ProductUsage.objects.get(usage__id=request.POST["product_usage_id"])
cart.add(pu)
return HttpResponse('{"success": true}')
return HttpResponse('{"success": false}')
def cart(request, cart_id):
cart = Cart.objects.get(id=cart_id)
entries = cart.entries.all()
context = {'cart': cart, "entries": entries}
return render(request, 'parts/cart.html', context)
def cart_entry(request, cart_id, entry_id):
cart = Cart.objects.get(id=cart_id)
entry = CartEntry.objects.get(id=entry_id)
context = {'cart': cart, 'entry': entry}
return render(request, 'parts/cart_entry.html', context)

View File

@ -1,2 +1,2 @@
django==3.0 django==3.0.5
psycopg2==2.8.4 psycopg2==2.8.4