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
command: python3 ./manage.py runserver 0.0.0.0:8000
ports:
- 8080:8000
- 8081:8000
depends_on:
- db
db:

View File

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

View File

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

View File

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

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>
<h3>{{part.name}}</h3>
<h3>{{part.get_name}}</h3>
<h4>{{full_name}}</h4>
<table>
<tr>
<th>Produkt</th>

View File

@ -4,3 +4,11 @@
<li><a href="{% url 'parts:product' product.id %}">{{product.name}}</a></li>
{% endfor %}
</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>
<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>
<ul>
{% for usage in sketch.usage_set.all %}
<li><a href="/parts/usage/{{usage.id}}/">{{usage}}</a></li>
<li><a href="% url 'parts:usage' usage.id %">{usage}</a></li><!--TODO!-->
<!--<li><a href="/parts/usage/{{usage.id}}/">{{usage}}</a></li>-->
<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 %}
</ul>
<p>{{sketch.get_brand}}</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>

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', views.sketch_add, name="new_sketch"),
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):
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)
@ -31,7 +34,8 @@ def sketch(request, sketch_id):
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'))
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)
def part(request, part_id):
@ -179,3 +183,28 @@ def get_quantity(qnty_str):
def is_exact_sketchnumber(sketch_number):
approximate = ("ähn", "vgl", "~", "zu")
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