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

View File

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

View File

@ -15,6 +15,7 @@ import os
import pandas as pd import pandas as pd
from django.conf import settings from django.conf import settings
from icecream import ic from icecream import ic
from decimal import Decimal
ic.configureOutput(includeContext= True) ic.configureOutput(includeContext= True)
@ -197,21 +198,40 @@ def save_inv(request):
del data['other_rate'] del data['other_rate']
print(f'others:{others}') 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: try:
df = pd.DataFrame(data) df = pd.DataFrame(data)
print(df) print(df)
df = df[df['TxnLineID'].isin(so_field)].reset_index()
print(df)
except: except:
print(Exception) print(Exception)
return HttpResponse("DataFrame Error") return HttpResponse("DataFrame Error")
# print(f'json:{df.to_json(orient="records")}') # print(f'json:{df.to_json(orient="records")}')
web_dict = df.to_dict("records") web_dict = df.to_dict("records")
# print(f'web_dict:{web_dict}' ) # 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}' ) # print(f'Sorted web_dict:{web_dict}' )
if ('so_field' in request.POST) and ('customer_fullname' in request.POST): if ('so_field' in request.POST) and ('customer_fullname' in request.POST):
print(request.POST.getlist('so_field')) 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') customer_fullname = request.POST.get('customer_fullname')
print(f'Customer_fullname:{customer_fullname} -> request values:{open_sales_orders_TxnID}') print(f'Customer_fullname:{customer_fullname} -> request values:{open_sales_orders_TxnID}')
### get the SO detail ### get the SO detail
@ -246,28 +266,34 @@ def save_inv(request):
for tu_line_ret in tu_['SalesOrderLineRet']: for tu_line_ret in tu_['SalesOrderLineRet']:
# print(tu_line_ret) # print(tu_line_ret)
if web['TxnLineID']==tu_line_ret['TxnLineID'] and web['ItemFullName']==tu_line_ret['ItemFullName']: 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') # print('put in list')
discPerPcs=0 discPerPcs=0
discPerItem=0 discPerItem=0
if float(web['rate']) == float(tu_line_ret['Rate']): if webrate == Decimal(tu_line_ret['Rate']):
discPerPcs = ini.get_discperpcs(web['ItemFullName'], float(web['rate'])) discPerPcs = ini.get_discperpcs(web['ItemFullName'], webrate)
elif float(web['rate']) < float(tu_line_ret['Rate']): elif webrate < Decimal(tu_line_ret['Rate']):
discPerPcs = float(tu_line_ret['Rate']) - float(web['rate']) discPerPcs = Decimal(tu_line_ret['Rate']) - webrate
discPerItem = float(web['backordered']) * discPerPcs discPerItem = webbackordered * discPerPcs
disc_amount += discPerItem disc_amount += discPerItem
SalesOrderLinedict = {'TxnLineID':web['TxnLineID'], SalesOrderLinedict = {'TxnLineID':web['TxnLineID'],
'ItemFullName':web['ItemFullName'], 'ItemFullName':tu_line_ret['ItemFullName'],
'Quantity':tu_line_ret['Quantity'], 'Quantity':tu_line_ret['Quantity'],
'UOM':tu_line_ret['UOM'], 'UOM':tu_line_ret['UOM'],
'Rate':float(web['rate']), 'Rate':webrate,
'Amount':float(tu_line_ret['Amount']), 'Amount':Decimal(tu_line_ret['Amount']),
'BackOrdered':float(web['backordered']), 'BackOrdered':webbackordered,
'Invoiced':tu_line_ret['Invoiced'], 'Invoiced':tu_line_ret['Invoiced'],
'LineIsManuallyClosed':tu_line_ret['LineIsManuallyClosed'], 'LineIsManuallyClosed':tu_line_ret['LineIsManuallyClosed'],
'discPerItem':discPerItem, # backorder qty * disc per pcs 'discPerItem':discPerItem, # backorder qty * disc per pcs
'discPerPcs':discPerPcs, 'discPerPcs':discPerPcs,
'convertQTY':tu_line_ret['convertQTY']
} }
# print(f'salesorderlineddict:{SalesOrderLinedict}') # print(f'salesorderlineddict:{SalesOrderLinedict}')
_salesorderlineret = dict_['SalesOrderLineRet'] _salesorderlineret = dict_['SalesOrderLineRet']
@ -277,7 +303,11 @@ def save_inv(request):
break break
break break
print(f'last:{dict_}') 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 dict_['Disc_Amount']=disc_amount
data_to_save.append(dict_) data_to_save.append(dict_)
print('save the last dict') print('save the last dict')
@ -296,9 +326,14 @@ def save_inv(request):
print(invoiceaddQBXML) print(invoiceaddQBXML)
result=None result=None
result = ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML()) # result = ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML())
print("RESULT:") print("RESULT:")
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) return render(request, "Invoice/so_details_form.html", context)
else: else:
return HttpResponse(f"You cannot Save, because There Is No Open Sales Order for Customer: {customer_fullname}") 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 charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <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"> <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 %} <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"> <link href= {% static 'bootstrap.min.css' %} rel="stylesheet" crossorigin="anonymous">
<script src= {% static 'bootstrap.bundle.min.js' %} ></script> <script src= {% static 'bootstrap.bundle.min.js' %} ></script>
<script src="https://unpkg.com/htmx.org@1.9.6"></script> <script src="https://unpkg.com/htmx.org@1.9.6"></script>
@ -83,6 +83,17 @@
</div> </div>
</div> </div>
</nav> </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 %} {% block body %}{% endblock body %}