from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.template import loader
from datetime import date,timedelta,datetime
from django.utils.timezone import now
from django.shortcuts import render,redirect,get_object_or_404
from django.urls import reverse,reverse_lazy
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.http import HttpResponse,HttpResponseRedirect,Http404,QueryDict
from django.contrib import messages
from django.db.models import F,Q
import csv,codecs
from django.db import models
from core.functions import get_account_name,empty_rack_slots,get_account_id,addition_check
from core.models import Account,Addition,Bottle,ShipUnit,Style,WineLocationValues
from core.models import Location,LocationType
from notes.models import Notes
import importlib
from itertools import chain
from core.functions import allocate
from notes.functions import create_note,add_note_message
from .forms import OrderForm
[docs]
def winestyles(request,style_rep):
#Get all wines of the style selected
# iterate through each location type in the template
# 8 June - update for Account feature
# get account details
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
filter = Style.objects.get(pk = style_rep)
wlv = WineLocationValues.objects.filter(style=filter.pk,account=account_id)
wlv_count=wlv.count()
print('style_rep = ',style_rep)
print('filter name = ',filter.name)
print('number of wines found =',wlv_count)
context = {'style' : filter ,
'style_count': wlv_count,
'locationtypes' : LocationType.objects.exclude(pk=1).order_by('pk'),
'wines' : wlv}
return render(request, 'reports/winestyles.html',context)
[docs]
def stylestest(request):
#styles = style.objects.all()
#context = {"styles": styles,
# }
return render(request, 'base/test_select.html')
[docs]
@login_required(login_url='login')
def audit_report(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(
content_type="text/csv;charset=UTF-8",
headers={"Content-Disposition": 'attachment; filename="Cellar Wine Audit.csv"'},
)
with codecs.open('Cellar Wine Audit.csv', 'w', 'utf-8-sig') as csvfile:
writer = csv.writer(response)
writer.writerow(["Location Type"," Location Name",
"Location ID","Wine",
"Wine ID","Quantity", "Style","Style ID",
"Year","Country","Size"])
report = Addition.objects.prefetch_related('wine_location')
style_details =Style.objects.all
locations = Location.objects.exclude(type_id='1').order_by('type_id',"name")
for l in locations :
print(l.name,l.location_qty)
if l.location_qty == 0: #report empty locations
writer.writerow([l.type,
l.name,
l.pk,
'empty',
'-',
'-',
'-',
'-',
'-',
'-',
])
else:
s=l.addition_set.all()
for a in s :
style_detail_id = Style.objects.get(name=a.style)
writer.writerow([l.type,
l.name,
l.pk,
a.name,
a.pk,
a.quantity,
a.style,
style_detail_id.pk,
a.year,
a.country,
a.size,
])
return response
[docs]
@login_required(login_url='login')
def open_orders(request):
# 8 June - update for Account feature
# get account details
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
orders = Addition.objects.all().filter(addition_status = 'Ordered',account=account_id) # have used 4 as Received is 5
context = {"orders": orders,
}
return render(request, 'reports/open_orders.html',context)
[docs]
@login_required(login_url='login')
def receiveorder(request,id):
# Documentation
"""
Overview
Set an Order to Recieved.
- Wine status set to received
- Create a temp location record - so we can put away (see addtion process).
- Create wine.location record to support M2M processes
- Update wine note
"""
#================================
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
order = Addition.objects.get(pk=id)
order.addition_status = 'Received'
order.save()
temp_location = Location(name = order.id,
location_qty = order.quantity,
type_id = '1',
)
temp_location.save()
order.wine_location.add(temp_location)
message = 'Wine Received'
add_note_message(order.id,message,account_id)
#create_note('R',account_id,order.id)
message = 'Order for ' + order.name + ' is now ready for put away. Wine Note updated'
messages.success(request, message)
response = redirect('/')
return response
[docs]
@login_required(login_url='login')
def put_away_list(request):
#Added account 8 June
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
#build list of addition records that are received or part allocated
received = Addition.objects.filter(addition_status= 'Received',account=account_id )
part_alloc = Addition.objects.filter(addition_status= 'Part Allocated')
result_list = list(chain(part_alloc, received))
if len(result_list)==0:
message = ' All wines have been put away'
# next = 'index'
next = '/'
messages.success(request, message)
#return render(request, './home')
return redirect(next) # redirect to put away list after process
else:
context = {
'content' : result_list,
}
template = loader.get_template('reports/put_away_list.html')
return HttpResponse(template.render(context, request))
#return HttpResponse('Simple Dev screen response')
[docs]
def test(request):
pass
[docs]
def wine_update(request):
pass
[docs]
@login_required(login_url='login')
def add_wine(request):
# Documentation
"""
Retail additions - these wines are received immediately.
- set status to received status
- add to a temp bin.
- temp bin name is the same as as the wine id to suppurt put away
# initialise required items so we can check they have been set
- create anote with associated text
"""
# ===========================================
# get account details
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
f_add_type = "Retail"
valid = 'true'
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
if request.method == "POST": # validate form and if valid add wine
f_name = request.POST['name']
f_style = request.POST['winestyle']
f_style_key = (f_style)
f_country = request.POST['country']
f_size = request.POST['winesize']
f_vintage = request.POST['vintage']
f_unit_price = request.POST['unit_price']
f_unit_price = float(f_unit_price)
f_unit_price = "%.2f" % f_unit_price # convert unit price to monetary format
f_del_unit = request.POST['del_unit'] # gives qty in a del unit
f_del_qty = request.POST['del_qty']
f_supplier_name = request.POST['supplier_name']
calc_qty = int(f_del_unit) * int(f_del_qty) # - calc qty
f_del_unit_type= ShipUnit.objects.get(qty=f_del_unit) # convert the qty to ship type
f_order_ref = "Retail"
f_duty ="0"
f_del_charges ="0"
f_del_date = datetime.now()
f_abv = ".00"
f_vat = "True"
f_addition_status = "Received"
f_account = account_id
##### prep for addition save ...
if valid == 'true':
new_wine = Addition(name = f_name,
style_id = f_style,
country_id= f_country,
year=f_vintage,
size_id = f_size,
price=f_unit_price,
abv=f_abv,
source = f_supplier_name,
duty = f_abv,
vat = f_vat,
quantity = calc_qty, # check quantity
delivery_qty = f_del_qty,
delivery_unit_id = f_del_unit_type.id, #> 1 unit implies case
order_ref = f_order_ref,
ship_costs = f_del_charges,
expected_delivery = f_del_date,
addition_status = f_addition_status,
account = f_account
)
#print('account=',f_account)
##### save to addition
new_wine.save()
##### Get the new wine details for Note creation
w_id= new_wine.id
w_account = new_wine.account
new_location =Location(name = new_wine.id,
location_qty = calc_qty,
type_id = '1',
)
new_location.save()
new_wine.wine_location.add(new_location)
#create a note for the wine
print('Wine id =',w_id)
create_note('A',w_account,w_id)
print ('line 276 - params :',account_id,w_id )
#### create user messages
# todo - move this logic to functions
#
bottle_size = Bottle.objects.get(id=f_size)
if calc_qty > 1:
message =f'{calc_qty} Bottles({bottle_size.cl} cl) of {f_name}'
else:
message =f'A Bottle({f_size}) of {f_name}'
print('addition status = ',f_addition_status )
if f_addition_status == "Received":
message = f'{message} added. Now you need to put away in a location.'
next = '/' # change to put away when ready
else:
message = f'{message} ordered.'
next = '/' # change to put away when ready
messages.success(request, message)
return redirect(next) # redirect to put away when ready
else: # invalid form
print('Error -invalid form No Post')
messages.error(request, message)
template = loader.get_template('reports/add_wine.html')
return HttpResponse(template.render({}, request))
else: # Display Form for 1st time
print('Initial Form Display')
template = loader.get_template('reports/add_wine.html')
return HttpResponse(template.render({}, request))
[docs]
@login_required(login_url='login')
def put_away(request,wine_id):
# Start
#Added account 8 June
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
#print('AC ID =',account_id.id)
putaway_selection ='empty'
bin_selection = 'empty' #populated after Bin chosen and Bin selected
capacity_chk = 1 # [todo work out case restrictions]
bin_capacity_chk = 0 # Flag set to True if bin capacity available for allocation
rack_capacity_chk = 0 # Flag set to True if rack capacity available for allocation
wine = Addition.objects.get(id = wine_id)
left_to_alloc = wine.quantity-wine.allocation_qty
# get the spare bin and rack capacity
#free_bin,free_rack = type_capacity(account_id) # gets total spare capacity by type
#print('free rack count=',free_rack)
binlist = Location.objects.filter(account_id=account_id,type_id=2)
free_bin = binlist.count()
free_bin = free_bin*99
racklist = empty_rack_slots(account_id)
free_rack = len(racklist)
print('free_rack =',free_rack)
#print('racklist =',racklist)
# check bin and rack capacity available
# case capacity:1,case & bin:2, case & rack: 4,case, bin and rack = 5
if free_bin > left_to_alloc:
capacity_chk = capacity_chk + 1 # Bin capacity = +1
if free_rack > 0 : # allows part allocation to rack(s) and rest to bin/case
capacity_chk = capacity_chk + 3 # Rack capacity = +3
context = {
'wine' : wine,
'to_allocate' : left_to_alloc,
'free_bin' : free_bin,
'free_rack' : free_rack,
'capacity_chk' : capacity_chk,
'putaway_selection': putaway_selection,
'binlist': binlist,
'racklist' : racklist
}
if request.method == "POST":
# Get Put away choices
# Get putaway type selected
if 'putaway_selection' in request.POST: # process the putaway choice
putaway_selection = request.POST['putaway_selection']
context.update({'putaway_selection': putaway_selection,})
else: # User has submitted form without selecting a putaway option
putaway_selection = False
# Process putaway selection
if putaway_selection == 'Case': # Create a case and put remaining wine away in the case
allocate(wine_id,'Case','opt',account_id)
#Create a put away note in cellar notes
message = f' {wine.quantity} bottle(s) of {wine.name} put away.'
next = 'put_away_list'
messages.success(request, message)
return redirect(next) # redirect to put away list after process
elif putaway_selection == 'Bin': #Display bins and then put remaining wine away in the selected Bin
if 'bin_selection' in request.POST: # bin has been selected so allocate remainder to the bin
bin_selection = request.POST['bin_selection']
bin_name = Location.objects.get(type_id='2',name=bin_selection,account=account_id.id)
allocate(wine_id,'Bin',bin_name.name,account_id)
message='Put Away'
add_note_message(wine_id,message,account_id)
#create_note('P',account_id,wine_id)
print('create note type P successful')
message = f' {wine.quantity} bottle(s) of {wine.name} put away.'
messages.success(request, message)
next = 'put_away_list'
return redirect(next) # redirect to put away list after process
else: # No bin selected, display bins for put away
context.update({'putaway_selection': putaway_selection})
template = loader.get_template('reports/putaway.html')
return HttpResponse(template.render(context, request))
elif putaway_selection == 'Rack': #Put 1 bottle away in an empty rack slot
#print('put away selection = ',putaway_selection)
if 'rack_selection' in request.POST:
rack_selection = request.POST['rack_selection']
print('rack selection = ',rack_selection)
rack_name = Location.objects.get(type_id='4',name=rack_selection,account=account_id)
allocate(wine_id,'Rack',rack_name.name,account_id)
# get the latest allocation values for wine and update context
wine = Addition.objects.get(id = wine_id)
context.update({'putaway_selection': putaway_selection,'racklist':racklist,
'wine':wine})
#Create a put away note in cellar notes
create_note('P',account_id,wine_id)
next = 'put_away_list'
return redirect(next) # redirect to put away list after process
else:
template = loader.get_template('reports/putaway.html')
return HttpResponse(template.render(context, request))
#template = loader.get_template('reports/putaway.html')
#return HttpResponse(template.render(context, request))
create_note('P',account_id,wine_id)
#template = loader.get_template('reports/putaway.html')
else: # Putaway type has not been selected
message = 'You must select a put away type '
next = 'put_away_list'
messages.error(request, message)
#return redirect(next) # redirect to put away list after process
template = loader.get_template('reports/putaway.html')
return HttpResponse(template.render(context, request))
template = loader.get_template('reports/putaway.html')
return HttpResponse(template.render(context, request))
context.update({'put_away_selection': putaway_selection})
template = loader.get_template('reports/putaway.html')
return HttpResponse(template.render(context, request))
[docs]
@login_required(login_url='login')
def drink(request,int):
### Account release - Added account details
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
wlv = WineLocationValues.objects.get(pk=int)
print('wlv wine id =',wlv.wine)
wine = Addition.objects.get(id = wlv.wine_id,account=account_id)
location = Location.objects.get(id=wlv.location_id,account=account_id)
upd_status = ''
note_msg = ''
my_flag = ''
my_message =''
context = {
'wine' : wine,
'location' : location,
'upd_status' : upd_status,
'note_msg' : note_msg,
'my_flag' : my_flag,
'my_message' : my_message,
}
# Reduce Addition qty by 1. If qty = 0, set to drunk else Tasted
qty = wine.quantity
qty = qty -1
if qty < 0: # Check if this is the last bottle to update addition status
qty = 0
status = 'Drunk'
elif qty == 0:
status = 'Drunk'
else:
status = 'Tried'
wine.quantity = qty
wine.addition_status= status
# Reduce Location Quantity by 1
qty = location.location_qty
print ('location qty =',qty,flush=True)
qty = qty -1
if qty < 0:
qty = 0
location.location_qty = qty
try: # Update Wine - Wine, M2M, WLV and add Note
try:
wine.save()
print('wine saved and id =',wine.id,flush=True)
except:
print ('wine save failed ',flush=True)
try:
location.save()
print('location saved',flush=True)
except:
print('location save failed ',flush=True)
# Update (M2M) addition_location
if location.location_qty ==0:
try:
print ('Remove M2M record',flush=True)
wine.wine_location.remove(location)
upd_status = 'M2M Update Success'
context.update({'upd_status' : upd_status,})
print (' M2M record removed',flush=True)
except:
print ('Failed in Remove M2M record',flush=True)
# Update or delete WineLocationValues
#
# if qty = 1 then delete ( so we can create new records)
# if qty > 1 then reduce by 1
if wlv.quantity == 1:
try:
wlv.delete()
print('Reports - Drink View SUCCESS wlv record deleted')
except:
print('Reports - Drink View ERROR wlv delete failed')
else :
#take 1 away
wlv_qty = wlv.quantity
wlv_qty = wlv_qty -1
wlv.quantity = wlv_qty
try:
wlv.save()
print('Reports - Drink View SUCCESS WLV record updated ',flush=True)
except:
print('Reports - Drink View ERROR unable to update WLV record',flush=True)
# Create Wine Note
#
try:
try:
print('call create note',account_id,wine.pk)
#create_note('C',account_id,wine.pk)
message = 'Wine Drunk'
add_note_message(wine.pk,message,account_id)
except:
print('create note failed !')
message = f' {account_id} {wine.name} set to drunk and a tasting note created.'
#my_flag = 'I'
# go back to winelist
next = '/reports/winelist'
messages.success(request, message)
return redirect(next) # redirect to put away list after process
except:
print('Drink - notes updates.Fail line 538 in reports view ',flush=True)
upd_status = 'Update Failed ! '
message = 'Drink - notes updates.Fail line 538 in reports view'
messages.error(request, message)
context.update({'update status' : upd_status,})
except:
print('Drink - notes updates.Fail line 545 in reports view ',flush=True)
upd_status = 'Update Failed ! '
message = 'Drink - notes updates.Fail line 540 in reports view'
messages.error(request, message)
context.update({'update status' : upd_status,})
return render(request, 'reports/winelist.html')
#template = loader.get_template('reports/drink.html')
#return HttpResponse(template.render(context, request))
[docs]
@login_required(login_url='login')
def createorder(request):
# get account details
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
# create object of form
context ={}
form = OrderForm(request.POST or None, request.FILES or None,
initial={'quantity': '0',
'addition_status':'Ordered',
'account': account_id})
context['form'] = form
if request.method == 'POST':
order_post = OrderForm(request.POST)
#print(request.POST)
# check if form data is valid
if form.is_valid():
order=form.save(commit=False)
shipkey = form.cleaned_data.get('delivery_unit')
shipqty=ShipUnit.objects.get(ship_unit=shipkey).qty
del_unit_qty = form.cleaned_data.get('delivery_qty')
order.quantity=int(shipqty) * int(del_unit_qty)
order.account =account_id
msg = form.cleaned_data.get('order_ref')
# save the form data to model
order.save()
#create a note for the wine
w_id= order.id
print('Wine id =',w_id)
create_note('O',order.account,w_id)
print ( 'line 569 - params :',account_id,w_id )
msg = 'Order Ref : ' + form.cleaned_data.get('order_ref') + ' saved.'
response = redirect('/')
return response
else:
print('notes update is not valid line 575')
context = {'form':form,
}
return render(request, 'reports/order-form.html',context)
return render(request, "reports/order-form.html", context)
[docs]
@login_required(login_url='login')
def winelist(request):
#Added account 8 June
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
wines = WineLocationValues.objects.filter(quantity__gt=0,account=account_id.id)
context = {'wines' : wines}
return render(request, 'reports/winelist.html',context)
[docs]
@login_required(login_url='login')
def empty_slots(request):
#Updated for Account Release
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
slot_list=empty_rack_slots(account_id) # Create the list
num_slots=len(slot_list)
context = {'slot_list' : slot_list,
'num_slots': num_slots}
return render(request, 'reports/emptyslots.html',context)
[docs]
@login_required(login_url='login')
def addition_errors(request):
#Moved to separate page 16 Aug
account_name = get_account_name(request)
account_id= Account.objects.get(account=account_name)
owner_id= int(account_id.id)
addition_status,errors = addition_check(owner_id)
return render(request, "reports/addition_error_list.html",
{'errors' : errors,
})