This commit is contained in:
bcomsugi 2023-12-30 06:08:09 +07:00
parent 56594e963a
commit 67ab69750d
4 changed files with 89 additions and 36 deletions

View File

@ -9,7 +9,7 @@ import timeit
import os
import pythoncom
# from icecream import ic
from decimal import Decimal
# ic.configureOutput(includeContext=True, )
class SalesOrderQuery:
@ -204,6 +204,7 @@ class SalesOrderQuery:
# Memo = self.create_sub_element(ET, InvoiceAdd, "Memo", self.DN['Memo'], 10 )
disc_amount = 0
for soidx, salesorder in enumerate(self.SalesOrderList):
disc_amount = 0
if 'TxnID' in salesorder:
SOTxnId = salesorder['TxnID']
# disc_amount+=int(salesorder['Disc_Amount'])
@ -254,8 +255,8 @@ class SalesOrderQuery:
# print(f"replyfrom qbxml:{self.connect_to_quickbooks(qbxml_query)}")
# print([s for s in qbxml_query.split('\n') if s.strip(' ') != ''])
print(self.pprintXml(qbxml_query))
return qbxml_query
# print(self.pprintXml(qbxml_query))
return self.pprintXml(qbxml_query)
def connect_to_quickbooks(self, qbxml_query):
@ -306,7 +307,7 @@ class SalesOrderQuery:
else:
return False, status_code
def get_discperpcs(self, ItemFullName, Rate:float):
def get_discperpcs(self, ItemFullName, Rate):
discPerPcs = 0
if self.Customer:
if self.Customer[2]:
@ -321,7 +322,7 @@ class SalesOrderQuery:
def _get_sales_order_header(self, response_string, includefullInvoiced=False):
print('_get_sales_order_header')
# print(f'responsestring:{self.pprintXml(response_string)}')
print(f'responsestring sales order header:{self.pprintXml(response_string)}')
QBXML = ET.fromstring(response_string)
datadict = {}
SalesOrderdict = {}
@ -360,27 +361,36 @@ class SalesOrderQuery:
for SalesOrderLineRet in SalesOrderLineRet:
discPerItem = 0
discPerPcs = 0
convertQTY = 1
TxnLineID = SalesOrderLineRet.find('TxnLineID').text
ItemFullName = SalesOrderLineRet.find('ItemRef/FullName')
if ItemFullName is None:
print("no itemfullname")
break
continue
else:
ItemFullName=ItemFullName.text
# print(ItemFullName)
Quantity = SalesOrderLineRet.find('Quantity').text
UnitOfMeasure = SalesOrderLineRet.find('UnitOfMeasure').text
### modified if UOM has ConvertQTY: '_' or ' of '-> in OverrideUOMSetRef
if '_' in UnitOfMeasure:
convertQTY = int(UnitOfMeasure.split('_')[1])
OverrideUOMSetRef = SalesOrderLineRet.find('OverrideUOMSetRef/FullName')
if OverrideUOMSetRef != None:
OverrideUOMSetRef = OverrideUOMSetRef.text
Rate = float(SalesOrderLineRet.find('Rate').text)
if 'of' in OverrideUOMSetRef and UnitOfMeasure.upper() == 'BOX':
convertQTY = int(OverrideUOMSetRef.split('of')[1])
print(f'OverrideUOMSetRef:{OverrideUOMSetRef}')
###
Amount = float(SalesOrderLineRet.find('Amount').text)
Rate = Decimal(SalesOrderLineRet.find('Rate').text)
Amount = Decimal(SalesOrderLineRet.find('Amount').text)
# if self.SPPriceLevelName:
Invoiced = SalesOrderLineRet.find('Invoiced').text
LineIsManuallyClosed = SalesOrderLineRet.find('IsManuallyClosed').text
# print(TxnLineID, ItemFullName)
BackOrdered = float(Quantity) - float(Invoiced)
BackOrdered = Decimal(Quantity) - Decimal(Invoiced)
if BackOrdered > 0 and LineIsManuallyClosed.lower() == 'false' :
# ic(self.Customer)
discPerPcs = self.get_discperpcs(ItemFullName, Rate)
@ -408,6 +418,7 @@ class SalesOrderQuery:
'LineIsManuallyClosed':LineIsManuallyClosed,
'discPerItem':discPerItem, # backorder qty * disc per pcs
'discPerPcs':discPerPcs,
'convertQTY':convertQTY,
}
SalesOrderdict['SalesOrderLineRet'].append(SalesOrderLinedict)
SalesOrderdict['Disc_Amount']=disc_amount

View File

@ -26,12 +26,8 @@
</thead>
<tbody>
{% for so_dict in objects %}
{{so_dict}}
{{so_dict.RefNumber}}
<!-- {{so_dict}} -->
{% for so_line in so_dict.SalesOrderLineRet %}
{{so_dict.RefNumber}}
{{so_line.ItemFullName}}
<tr>
<td>
<input type="hidden" name="RefNumber" value="{{ so_dict.RefNumber }}">
@ -43,18 +39,18 @@
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endfor %}
<div class="form-check">
<input type="checkbox" name="so_field" value="{{ so_dict.TxnID }}" id="id_so_field_{{forloop.counter}}" class="form-check-input" checked>
<input type="checkbox" name="so_field" value="{{ so_line.TxnLineID }}" id="id_so_field_{{forloop.counter}}" class="form-check-input" checked>
<label for="id_so_field_{{ forloop.counter }}" class="form-check-label">{{so_line.ItemFullName}}
</label>
</div>
</td>
<td>{{ so_dict.TxnDate }}</td>
<td>{{ so_dict.RefNumber }}</td>
<td>{{ so_line.Quantity }}</td>
<td>{{ so_line.Invoiced }}</td>
<td><input type="number" name="backordered" required value="{{ so_line.BackOrdered }}" min="0" max="{{ so_line.BackOrdered }}"> </td>
<td>{% widthratio so_line.Quantity so_line.convertQTY 1 %}</td>
<td>{% widthratio so_line.Invoiced so_line.convertQTY 1 %}</td>
<td><input type="number" name="backordered" required value="{% widthratio so_line.BackOrdered so_line.convertQTY 1 %}" min="0" max="{% widthratio so_line.BackOrdered so_line.convertQTY 1 %}"> </td>
<td>{{ so_line.UOM }}</td>
<td><input type="number" name="rate" required value="{{ so_line.Rate }}" min="0" > </td>
<td><input type="number" name="rate" required value="{% widthratio so_line.Rate 1 so_line.convertQTY %}" min="0" > </td>
</tr>
{% endfor %}
{% endfor %}

View File

@ -15,6 +15,7 @@ import os
import pandas as pd
from django.conf import settings
from icecream import ic
from decimal import Decimal
ic.configureOutput(includeContext= True)
@ -197,21 +198,40 @@ def save_inv(request):
del data['other_rate']
print(f'others:{others}')
# print(f"datadict:{data}")
print(f"datadict:{data}")
required_data = ['RefNumber', 'CustomerFullName', 'TxnID', 'TxnDate', 'TotalAmount', 'TxnLineID', 'ItemFullName',
'Quantity', 'UOM', 'Rate', 'Amount', 'Invoiced', 'LineIsManuallyClosed', 'backordered', 'rate', 'convertQTY']
if 'so_field' not in data:
return HttpResponse("no Selected Items. Please select at least 1 item to be invoiced.")
for _ in required_data:
if _ not in data:
return HttpResponse(f"Required data not returned from the form: {_}")
### retrieve only the selected so_field ###
so_field = data['so_field']
del data['so_field']
selectedItems = []
for _ in so_field:
if _ not in data['TxnLineID']:
return HttpResponse(f"One of the TxnLineID is not valid: {_}")
data_idx = data['TxnLineID'].index(_)
try:
df = pd.DataFrame(data)
print(df)
df = df[df['TxnLineID'].isin(so_field)].reset_index()
print(df)
except:
print(Exception)
return HttpResponse("DataFrame Error")
# print(f'json:{df.to_json(orient="records")}')
web_dict = df.to_dict("records")
# print(f'web_dict:{web_dict}' )
web_dict = sorted(web_dict, key=lambda x: x['TxnLineID'])
web_dict = sorted(web_dict, key=lambda x: x['TxnID'])
# print(f'Sorted web_dict:{web_dict}' )
if ('so_field' in request.POST) and ('customer_fullname' in request.POST):
print(request.POST.getlist('so_field'))
open_sales_orders_TxnID = unique(request.POST.getlist('so_field'))
# open_sales_orders_TxnID = unique(request.POST.getlist('so_field'))
open_sales_orders_TxnID = unique(df['TxnID'].to_list())
customer_fullname = request.POST.get('customer_fullname')
print(f'Customer_fullname:{customer_fullname} -> request values:{open_sales_orders_TxnID}')
### get the SO detail
@ -246,28 +266,34 @@ def save_inv(request):
for tu_line_ret in tu_['SalesOrderLineRet']:
# print(tu_line_ret)
if web['TxnLineID']==tu_line_ret['TxnLineID'] and web['ItemFullName']==tu_line_ret['ItemFullName']:
if 0 < float(web['backordered']) <= float(tu_line_ret['BackOrdered']):
### modified back the rate and backordered using convertQTY ###
convertQTY = int(tu_line_ret['convertQTY'])
webbackordered = Decimal(web['backordered']) * convertQTY
webrate = Decimal(web['rate']) / convertQTY
###
if 0 < webbackordered <= Decimal(tu_line_ret['BackOrdered']):
# print('put in list')
discPerPcs=0
discPerItem=0
if float(web['rate']) == float(tu_line_ret['Rate']):
discPerPcs = ini.get_discperpcs(web['ItemFullName'], float(web['rate']))
elif float(web['rate']) < float(tu_line_ret['Rate']):
discPerPcs = float(tu_line_ret['Rate']) - float(web['rate'])
discPerItem = float(web['backordered']) * discPerPcs
if webrate == Decimal(tu_line_ret['Rate']):
discPerPcs = ini.get_discperpcs(web['ItemFullName'], webrate)
elif webrate < Decimal(tu_line_ret['Rate']):
discPerPcs = Decimal(tu_line_ret['Rate']) - webrate
discPerItem = webbackordered * discPerPcs
disc_amount += discPerItem
SalesOrderLinedict = {'TxnLineID':web['TxnLineID'],
'ItemFullName':web['ItemFullName'],
'ItemFullName':tu_line_ret['ItemFullName'],
'Quantity':tu_line_ret['Quantity'],
'UOM':tu_line_ret['UOM'],
'Rate':float(web['rate']),
'Amount':float(tu_line_ret['Amount']),
'BackOrdered':float(web['backordered']),
'Rate':webrate,
'Amount':Decimal(tu_line_ret['Amount']),
'BackOrdered':webbackordered,
'Invoiced':tu_line_ret['Invoiced'],
'LineIsManuallyClosed':tu_line_ret['LineIsManuallyClosed'],
'discPerItem':discPerItem, # backorder qty * disc per pcs
'discPerPcs':discPerPcs,
'convertQTY':tu_line_ret['convertQTY']
}
# print(f'salesorderlineddict:{SalesOrderLinedict}')
_salesorderlineret = dict_['SalesOrderLineRet']
@ -277,7 +303,11 @@ def save_inv(request):
break
break
print(f'last:{dict_}')
if dict_ != data_to_save[-1]: #append the last dict_
print(f'datatosaveonly:{data_to_save}')
if not data_to_save:
dict_['Disc_Amount']=disc_amount
data_to_save.append(dict_)
elif dict_ != data_to_save[-1]: #append the last dict_
dict_['Disc_Amount']=disc_amount
data_to_save.append(dict_)
print('save the last dict')
@ -296,9 +326,14 @@ def save_inv(request):
print(invoiceaddQBXML)
result=None
result = ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML())
# result = ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML())
print("RESULT:")
print(result)
if result and ini.status_ok(result):
pass
messages.success(request, 'Invoice Has Been SAVED')
return redirect('Invoice:show_customer')
return render(request, "Invoice/so_details_form.html", context)
else:
return HttpResponse(f"You cannot Save, because There Is No Open Sales Order for Customer: {customer_fullname}")

View File

@ -6,8 +6,8 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% comment %} <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script> {% endcomment %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
<link href= {% static 'bootstrap.min.css' %} rel="stylesheet" crossorigin="anonymous">
<script src= {% static 'bootstrap.bundle.min.js' %} ></script>
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
@ -83,6 +83,17 @@
</div>
</div>
</nav>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message | safe }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{% endfor %}
{% endif %}
{% block body %}{% endblock body %}