#!/usr/bin/env python3 # -*- coding: utf-8 -*- # This file is part of the Taler Codeless Merchant. # (C) 2018 GNUnet e.V. # # The Taler Codeless Merchant is free software: you can redistribute it and/or # modify it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # The Taler Codeless Merchant is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License # for more details. # # You should have received a copy of the GNU Affero General Public License along # with the Taler Codeless Merchant. If not, see . # # @author Shivam Kohli from inventory.forms import SignUpForm, LoginForm, DocumentForm from inventory.models import Merchant, Product, Order from django.contrib.auth.models import User from django.contrib.auth import authenticate from django.contrib.auth import login as auth_login from django.contrib.auth import logout as auth_logout from django.shortcuts import get_object_or_404 from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect from django.core.urlresolvers import resolve import requests from urllib.parse import urljoin from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt import json from django.http import JsonResponse BACKEND_URL = "https://backend.demo.taler.net/" def mime_content_type(filename): """Get mime type """ mime_types = dict( txt='text/plain', htm='text/html', html='text/html', php='text/html', css='text/css', js='application/javascript', json='application/json', xml='application/xml', swf='application/x-shockwave-flash', flv='video/x-flv', # images png='image/png', jpe='image/jpeg', jpeg='image/jpeg', jpg='image/jpeg', gif='image/gif', bmp='image/bmp', ico='image/vnd.microsoft.icon', tiff='image/tiff', tif='image/tiff', svg='image/svg+xml', svgz='image/svg+xml', # archives zip='application/zip', rar='application/x-rar-compressed', exe='application/x-msdownload', msi='application/x-msdownload', cab='application/vnd.ms-cab-compressed', # audio/video mp3='audio/mpeg', ogg='audio/ogg', qt='video/quicktime', mov='video/quicktime', # adobe pdf='application/pdf', psd='image/vnd.adobe.photoshop', ai='application/postscript', eps='application/postscript', ps='application/postscript', # ms office doc='application/msword', rtf='application/rtf', xls='application/vnd.ms-excel', ppt='application/vnd.ms-powerpoint', # open office odt='application/vnd.oasis.opendocument.text', ods='application/vnd.oasis.opendocument.spreadsheet', ) ext = filename.split('.')[-1].lower() if ext in mime_types: return mime_types[ext] else: return 'application/octet-stream' def fulfillment(request): """ This function is responsible for redirecting to a page after successfully payment is done. For digital Inventory is displays the required document on payment completion. """ order_id = request.GET.get('order_id') if order_id is None: return HttpResponse("Error while loading the page") order_instance = Order.objects.get(order_id=order_id) product = order_instance.product_id.all() for item in range(len(product)): if product[item].document: filename = product[item].document.file.name.split('/')[-1] file = product[item].document.file mime = mime_content_type(filename) response = HttpResponse(file, content_type=mime) response['Content-Disposition'] = 'inline; filename=%s' % filename return response else: context_dict = {} context_dict['name'] = product[item].name context_dict['price'] = product[item].price context_dict['delivery_date'] = order_instance.order_date context_dict['address'] = order_instance.address merchant = order_instance.merchant user = User.objects.get(username=merchant) primary_key = user.pk merchant_instance = Merchant.objects.get(pk=primary_key) context_dict['website'] = merchant_instance.website if order_instance.status=='order_processing': context_dict['order_processing'] = 'True' context_dict['pre_production'] = 'False' context_dict['in_production'] = 'False' context_dict['shipped'] = 'False' context_dict['delivered'] = 'False' elif order_instance.status=='pre_production': context_dict['order_processing'] = 'True' context_dict['pre_production'] = 'True' context_dict['in_production'] = 'False' context_dict['shipped'] = 'False' context_dict['delivered'] = 'False' elif order_instance.status=='in_production': context_dict['order_processing'] = 'True' context_dict['pre_production'] = 'True' context_dict['in_production'] = 'True' context_dict['shipped'] = 'False' context_dict['delivered'] = 'False' elif order_instance.status=='shipped': context_dict['order_processing'] = 'True' context_dict['pre_production'] = 'True' context_dict['in_production'] = 'True' context_dict['shipped'] = 'True' context_dict['delivered'] = 'False' else: context_dict['order_processing'] = 'True' context_dict['pre_production'] = 'True' context_dict['in_production'] = 'True' context_dict['shipped'] = 'True' context_dict['delivered'] = 'True' return render(request, 'inventory/fulfillment.html', context_dict) def shipment(request): """ Function to redirect to the shipment detail form """ context_dict = {} name = request.GET.get('name') price = request.GET.get('price') merchant = request.GET.get('merchant') context_dict['name'] = name context_dict['price'] = price context_dict['merchant'] = merchant return render(request, 'inventory/shipment_details.html', context_dict) @login_required def order(request): """ To display all the purchases made by the merchant this function is invoked. """ user_instance = User.objects.get(username=request.user.username) order = Order.objects.filter(merchant=user_instance) context_dict = {} array = [] for i in order: data = {} data['order_id'] = i.order_id product = i.product_id.all() array_product = [] for item in range(len(product)): data_product = {} data_product["name"] = product[item].name array_product.append(data_product) data['array_product'] = array_product data['summary'] = i.summary data['order_date'] = i.order_date data['address'] = i.address data['status'] = i.status data['url'] = "/update_status/"+str(i.uid) array.append(data) context_dict['data'] = array return render(request, 'inventory/order.html', context_dict) @csrf_exempt def pay(request): """ The function is invoked by the wallet. In this function the csrf token in exemted. For proper inventory tracking this function is responsible to update the inventory only after the successfull payment completion. """ if request.method == 'POST': body_unicode = request.body.decode('utf-8') json_data = json.loads(body_unicode) if json_data is None: return HttpResponse("no json in body") r = requests.post("https://backend.demo.taler.net/public/pay", json=json_data, headers={"Authorization": "ApiKey sandbox"}) if r.status_code != 200: return HttpResponse(r.status_code) contract_terms = r.json()["contract_terms"] merchant = contract_terms["merchant"]["name"] order_instance = Order.objects.create( order_id=contract_terms["order_id"], summary=contract_terms["summary"], merchant=User.objects.get(username=merchant), status="order_processing", address=contract_terms["extra"]["address"] ) order_instance.save() for i in contract_terms['products']: product_instance = Product.objects.get(name=i["description"]) order_instance.product_id.add(product_instance) order_instance.save() update_inventory(i["description"], i["quantity"]) return JsonResponse(r.json()) def payment(request): """ This function is called when the user has submitted the shipment details. The order for the required product is made and a post request is send for creation of the order. On the basis of this a required order_id is returned which is used to send a get request and check the status of the order. On the basis of this the user is redirected to the payment page. """ session_id = request.session.session_key name = request.GET.get('name') price = request.GET.get('price') merchant = request.GET.get('merchant') # name_user = request.GET.get('name_user') address_user = request.GET.get('address_user') summary = name+' purchased from '+merchant user = User.objects.get(username=merchant) primary_key = user.pk merchant_instance = Merchant.objects.get(pk=primary_key) base_url = request.build_absolute_uri().rsplit('/', 1)[0] # Creating an Order for a Payment order = dict(order=dict(amount="KUDOS:"+price, summary=summary, extra=dict(address=address_user), products=[ dict( description=name, quantity=1, product_id=1, price="KUDOS:"+price, ), ], fulfillment_url=base_url+"/fulfillment/", pay_url=base_url+"/pay/", merchant=dict( address=merchant_instance.address, name=merchant, jurisdiction="none", instance="default", ), ) ) order_resp = backend_post("order", order) try: print(order_resp) order_id = order_resp["order_id"] except Exception as e: return HttpResponse("Please refresh and try again") # Checking Payment Status and Prompting for Payment pay_params = dict( instance="default", order_id=order_id, session_id=session_id, ) pay_status = backend_get("check-payment", pay_params) if pay_status["paid"]: base_url = request.build_absolute_uri().rsplit('/', 1)[0] url = base_url+"/fulfillment?order_id="+order_resp["order_id"] return redirect(url) else: if pay_status["payment_redirect_url"]: payment_redirect_url = pay_status["payment_redirect_url"] return redirect(payment_redirect_url) def backend_get(endpoint, params): headers = {"Authorization": "ApiKey sandbox"} try: resp_url = urljoin(BACKEND_URL, endpoint) resp = requests.get(resp_url, params=params, headers=headers) except requests.ConnectionError: return HttpResponse("Could not establish connection to backend") try: response_json = resp.json() except ValueError: return HttpResponse("Could not parse response from backend") return response_json def backend_post(endpoint, json): headers = {"Authorization": "ApiKey sandbox"} try: resp_url = urljoin(BACKEND_URL, endpoint) resp = requests.post(resp_url, json=json, headers=headers) print(resp) except requests.ConnectionError: return HttpResponse("Could not establish connection to backend") try: response_json = resp.json() except ValueError: return HttpResponse("Could not parse response from backend") return response_json def update_inventory(name, quantity): product_instance = Product.objects.get(name=name) if product_instance.document: pass else: inventory_on_hand = product_instance.inventory_on_hand - quantity product_instance.inventory_on_hand = inventory_on_hand product_instance.save() @login_required def home(request): """ Home page for the merchant where he can add and view all his inventory. """ user_instance = User.objects.get(username=request.user.username) product = Product.objects.filter(user=user_instance) context_dict = {} array = [] for i in product: data = {} data['name'] = i.name data['description'] = i.description data['price'] = i.price data['inventory_on_hand'] = i.inventory_on_hand data['url'] = '/home/product/' + str(i.product_id) if i.document: data['digital'] = 'True' else: data['digital'] = 'False' array.append(data) context_dict['data'] = array return render(request, 'inventory/home.html', context_dict) @login_required def update_stock(request, uid): product_instance = Product.objects.get(name=uid) product_instance.inventory_on_hand = request.POST.get('stock_updated') product_instance.save() base_url = request.build_absolute_uri().rsplit('/', 3)[0] return redirect(base_url+'home/product/'+str(product_instance.product_id)) @login_required def update_status(request, uid): order_instance = Order.objects.get(uid=uid) order_instance.status = request.POST.get('status') print(request.POST.get('status')) order_instance.save() return redirect('order') @login_required def add_product(request): """ When a merchant is required to add a new product in his inventory this function is invoked. """ name = request.POST.get('name') product_instance = Product.objects.get_or_create(name=name)[0] description = request.POST.get('description') product_instance.description = description price = request.POST.get('price') product_instance.price = float(price) starting_inventory = request.POST.get('starting_inventory') product_instance.starting_inventory = starting_inventory product_instance.inventory_on_hand = starting_inventory minimum_required = request.POST.get('minimum_required') product_instance.minimum_required = minimum_required current_merchant = request.user.username user_instance = User.objects.get(username=current_merchant) product_instance.user = user_instance product_instance.save() base_url = request.build_absolute_uri().rsplit('/', 3)[0] return redirect(base_url+'home/') @login_required def product(request, uid): """ The product display page for the Mercheant. The required product details are included in this page. """ item = Product.objects.get_or_create(product_id=uid)[0] context_dict = {} context_dict['name'] = item.name context_dict['description'] = item.description context_dict['price'] = item.price context_dict['inventory_on_hand'] = item.inventory_on_hand url_update_inventory = str('/update_stock/') + item.name context_dict['url_update_inventory'] = url_update_inventory base_url = request.build_absolute_uri().rsplit('/', 3)[0] merchant = request.user.username parameters = "name="+item.name+'&price='+str(item.price)+'&merchant='+merchant if item.document: context_dict['href'] = base_url+"/payment?"+parameters context_dict['digital'] = 'True' else: context_dict['href'] = base_url+"/shipment?"+parameters context_dict['digital'] = 'False' user_instance = User.objects.get(username=merchant) order = Order.objects.filter(merchant=user_instance) count = 0 for i in order: product = i.product_id.filter(name=item.name) count = count+1 context_dict['count'] = count return render(request, 'inventory/product.html', context_dict) @login_required def new_product(request): return render(request, 'inventory/new_product.html') @login_required def digital_inventory(request): if request.method == 'POST': form = DocumentForm(request.POST, request.FILES) if form.is_valid(): user_instance = User.objects.get(username=request.user.username) Product.objects.create( name=form.cleaned_data['name'], description=form.cleaned_data['description'], price=form.cleaned_data['price'], starting_inventory=0, minimum_required=0, inventory_on_hand=0, inventory_recieved=0, inventory_shipped=0, user=user_instance, document=form.cleaned_data['document'] ) return redirect('home') else: form = DocumentForm() return render(request, 'inventory/digital_inventory.html', { 'form': form }) def signup(request): if request.method == 'POST': form = SignUpForm(request.POST) if form.is_valid(): form.save() username = form.cleaned_data.get('username') raw_password = form.cleaned_data.get('password1') user = authenticate(username=username, password=raw_password) auth_login(request, user) instance = get_object_or_404(Merchant, user=user) instance.address = form.cleaned_data.get('address') instance.website = form.cleaned_data.get('website') if form.cleaned_data.get('pay_url'): instance.address = form.cleaned_data.get('pay_url') else: #payto URI will be made pass instance.save() return redirect('home') else: form = SignUpForm() dictionary = {'form': form} return render(request, 'inventory/signup.html', dictionary) def login(request): form = LoginForm() context_dict = {} context_dict['form'] = form error_message = "" if request.method == 'POST': username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: auth_login(request, user) current_url = resolve(request.path_info).url_name # Redirect to a success page. return redirect('home') else: error_message = "Incorrect username or password" form = LoginForm() context_dict['form'] = form context_dict['error_message'] = error_message return render(request, 'inventory/login.html', context_dict) def logout(request): auth_logout(request) return render(request, 'inventory/index.html')