mirror of
https://github.com/bcomsugi/dasaproject.git
synced 2026-01-09 15:12:37 +07:00
test
This commit is contained in:
commit
5854568f28
171
.gitignore
vendored
Normal file
171
.gitignore
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
*.pot
|
||||
*.pyc
|
||||
__pycache__/
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
DN_Excel_files/
|
||||
ItemInventory/
|
||||
QBbackup/
|
||||
.xlsx
|
||||
.pdf
|
||||
QBbackup/
|
||||
test_folder_source_DeliveryNote/
|
||||
9
DASAServer.bat
Normal file
9
DASAServer.bat
Normal file
@ -0,0 +1,9 @@
|
||||
cd "C:\Sources\dasaproject win7\"
|
||||
echo already cd
|
||||
cmd /k "cd /d C:\Sources\dasaproject win7\env\Scripts\ & activate & cd /d C:\Sources\dasaproject win7\ & uvicorn main:app --host 0.0.0.0 --port 9999
|
||||
echo workon env
|
||||
echo pause
|
||||
echo get to env
|
||||
echo cd "C:\Users\MSi\Documents\Python Project\DBW\Web scrapping"
|
||||
echo uvicorn main:app --reload
|
||||
echo start app
|
||||
BIN
DASAServer.bat - Shortcut.lnk
Normal file
BIN
DASAServer.bat - Shortcut.lnk
Normal file
Binary file not shown.
5
FASTApiItemReceipt.bat
Normal file
5
FASTApiItemReceipt.bat
Normal file
@ -0,0 +1,5 @@
|
||||
cd "C:\Sources\dasaproject win7\"
|
||||
echo already cd
|
||||
cmd /k "cd /d C:\Sources\dasaproject win7\env\Scripts\ & activate & cd /d C:\Sources\dasaproject win7\ & uvicorn main:app --host 0.0.0.0 --port 8888
|
||||
echo uvicorn main:app --reload
|
||||
echo start app
|
||||
146
GeneralSummaryReport.xml
Normal file
146
GeneralSummaryReport.xml
Normal file
@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?qbxml version="16.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<GeneralSummaryReportQueryRq>
|
||||
<!-- GeneralSummaryReportType may have one of the following values: BalanceSheetByClass, BalanceSheetPrevYearComp, BalanceSheetStandard, BalanceSheetSummary, CustomerBalanceSummary, ExpenseByVendorSummary, IncomeByCustomerSummary, InventoryStockStatusByItem, InventoryStockStatusByVendor, IncomeTaxSummary, InventoryValuationSummary, InventoryValuationSummaryBySite, LotNumberInStockBySite, PhysicalInventoryWorksheet, ProfitAndLossByClass, ProfitAndLossByJob, ProfitAndLossPrevYearComp, ProfitAndLossStandard, ProfitAndLossYTDComp, PurchaseByItemSummary, PurchaseByVendorSummary, SalesByCustomerSummary, SalesByItemSummary, SalesByRepSummary, SalesTaxLiability, SalesTaxRevenueSummary, SerialNumberInStockBySite, TrialBalance, VendorBalanceSummary -->
|
||||
<GeneralSummaryReportType >ENUMTYPE</GeneralSummaryReportType> <!-- required -->
|
||||
<DisplayReport >BOOLTYPE</DisplayReport> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ReportPeriod> <!-- optional -->
|
||||
<FromReportDate >DATETYPE</FromReportDate> <!-- optional -->
|
||||
<ToReportDate >DATETYPE</ToReportDate> <!-- optional -->
|
||||
</ReportPeriod>
|
||||
<!-- OR -->
|
||||
<!-- ReportDateMacro may have one of the following values: All, Today, ThisWeek, ThisWeekToDate, ThisMonth, ThisMonthToDate, ThisQuarter, ThisQuarterToDate, ThisYear, ThisYearToDate, Yesterday, LastWeek, LastWeekToDate, LastMonth, LastMonthToDate, LastQuarter, LastQuarterToDate, LastYear, LastYearToDate, NextWeek, NextFourWeeks, NextMonth, NextQuarter, NextYear -->
|
||||
<ReportDateMacro >ENUMTYPE</ReportDateMacro> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ReportAccountFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<!-- AccountTypeFilter may have one of the following values: AccountsPayable, AccountsReceivable, AllowedFor1099, APAndSalesTax, APOrCreditCard, ARAndAP, Asset, BalanceSheet, Bank, BankAndARAndAPAndUF, BankAndUF, CostOfSales, CreditCard, CurrentAsset, CurrentAssetAndExpense, CurrentLiability, Equity, EquityAndIncomeAndExpense, ExpenseAndOtherExpense, FixedAsset, IncomeAndExpense, IncomeAndOtherIncome, Liability, LiabilityAndEquity, LongTermLiability, NonPosting, OrdinaryExpense, OrdinaryIncome, OrdinaryIncomeAndCOGS, OrdinaryIncomeAndExpense, OtherAsset, OtherCurrentAsset, OtherCurrentLiability, OtherExpense, OtherIncome, OtherIncomeOrExpense -->
|
||||
<AccountTypeFilter >ENUMTYPE</AccountTypeFilter> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</ReportAccountFilter>
|
||||
<ReportEntityFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<!-- EntityTypeFilter may have one of the following values: Customer, Employee, OtherName, Vendor -->
|
||||
<EntityTypeFilter >ENUMTYPE</EntityTypeFilter> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</ReportEntityFilter>
|
||||
<ReportItemFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<!-- ItemTypeFilter may have one of the following values: AllExceptFixedAsset, Assembly, Discount, FixedAsset, Inventory, InventoryAndAssembly, NonInventory, OtherCharge, Payment, Sales, SalesTax, Service -->
|
||||
<ItemTypeFilter >ENUMTYPE</ItemTypeFilter> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</ReportItemFilter>
|
||||
<ReportClassFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</ReportClassFilter>
|
||||
<ReportTxnTypeFilter> <!-- optional -->
|
||||
<!-- TxnTypeFilter may have one of the following values: All, ARRefundCreditCard, Bill, BillPaymentCheck, BillPaymentCreditCard, BuildAssembly, Charge, Check, CreditCardCharge, CreditCardCredit, CreditMemo, Deposit, Estimate, InventoryAdjustment, Invoice, ItemReceipt, JournalEntry, LiabilityAdjustment, Paycheck, PayrollLiabilityCheck, PurchaseOrder, ReceivePayment, SalesOrder, SalesReceipt, SalesTaxPaymentCheck, Transfer, VendorCredit, YTDAdjustment -->
|
||||
<TxnTypeFilter >ENUMTYPE</TxnTypeFilter> <!-- required, may repeat -->
|
||||
</ReportTxnTypeFilter>
|
||||
<!-- BEGIN OR -->
|
||||
<ReportModifiedDateRangeFilter> <!-- optional -->
|
||||
<FromReportModifiedDate >DATETYPE</FromReportModifiedDate> <!-- optional -->
|
||||
<ToReportModifiedDate >DATETYPE</ToReportModifiedDate> <!-- optional -->
|
||||
</ReportModifiedDateRangeFilter>
|
||||
<!-- OR -->
|
||||
<!-- ReportModifiedDateRangeMacro may have one of the following values: All, Today, ThisWeek, ThisWeekToDate, ThisMonth, ThisMonthToDate, ThisQuarter, ThisQuarterToDate, ThisYear, ThisYearToDate, Yesterday, LastWeek, LastWeekToDate, LastMonth, LastMonthToDate, LastQuarter, LastQuarterToDate, LastYear, LastYearToDate, NextWeek, NextFourWeeks, NextMonth, NextQuarter, NextYear -->
|
||||
<ReportModifiedDateRangeMacro >ENUMTYPE</ReportModifiedDateRangeMacro> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<!-- ReportDetailLevelFilter may have one of the following values: All [DEFAULT], AllExceptSummary, SummaryOnly -->
|
||||
<ReportDetailLevelFilter >ENUMTYPE</ReportDetailLevelFilter> <!-- optional -->
|
||||
<!-- ReportPostingStatusFilter may have one of the following values: Either, NonPosting, Posting -->
|
||||
<ReportPostingStatusFilter >ENUMTYPE</ReportPostingStatusFilter> <!-- optional -->
|
||||
<!-- SummarizeColumnsBy may have one of the following values: Account, BalanceSheet, Class, Customer, CustomerType, Day, Employee, FourWeek, HalfMonth, IncomeStatement, ItemDetail, ItemType, Month, Payee, PaymentMethod, PayrollItemDetail, PayrollYtdDetail, Quarter, SalesRep, SalesTaxCode, ShipMethod, Terms, TotalOnly, TwoWeek, Vendor, VendorType, Week, Year -->
|
||||
<SummarizeColumnsBy >ENUMTYPE</SummarizeColumnsBy> <!-- optional -->
|
||||
<IncludeSubcolumns >BOOLTYPE</IncludeSubcolumns> <!-- optional -->
|
||||
<!-- ReportCalendar may have one of the following values: CalendarYear, FiscalYear, TaxYear -->
|
||||
<ReportCalendar >ENUMTYPE</ReportCalendar> <!-- optional -->
|
||||
<!-- ReturnRows may have one of the following values: ActiveOnly, NonZero, All -->
|
||||
<ReturnRows >ENUMTYPE</ReturnRows> <!-- optional -->
|
||||
<!-- ReturnColumns may have one of the following values: ActiveOnly, NonZero, All -->
|
||||
<ReturnColumns >ENUMTYPE</ReturnColumns> <!-- optional -->
|
||||
<!-- ReportBasis may have one of the following values: Accrual, Cash, None [DEFAULT] -->
|
||||
<ReportBasis >ENUMTYPE</ReportBasis> <!-- optional -->
|
||||
</GeneralSummaryReportQueryRq>
|
||||
|
||||
<GeneralSummaryReportQueryRs statusCode="INTTYPE" statusSeverity="STRTYPE" statusMessage="STRTYPE">
|
||||
<ReportRet> <!-- optional -->
|
||||
<ReportTitle >STRTYPE</ReportTitle> <!-- required -->
|
||||
<ReportSubtitle >STRTYPE</ReportSubtitle> <!-- required -->
|
||||
<!-- ReportBasis may have one of the following values: Accrual, Cash, None [DEFAULT] -->
|
||||
<ReportBasis >ENUMTYPE</ReportBasis> <!-- optional -->
|
||||
<NumRows >INTTYPE</NumRows> <!-- required -->
|
||||
<NumColumns >INTTYPE</NumColumns> <!-- required -->
|
||||
<NumColTitleRows >INTTYPE</NumColTitleRows> <!-- required -->
|
||||
<ColDesc colID="INTTYPE" dataType="ENUMTYPE"> <!-- required, may repeat -->
|
||||
<ColTitle titleRow="INTTYPE" value="STRTYPE"> <!-- required, may repeat -->
|
||||
</ColTitle>
|
||||
<!-- ColType may have one of the following values: Account, Addr1, Addr2, Addr3, Addr4, Addr5, Aging, Amount, AmountDifference, AverageCost, BilledDate, BillingStatus, Blank, CalculatedAmount, Class, ClearedStatus, CostPrice, CreateDate, Credit, CustomField, Date, Debit, DeliveryDate, DueDate, Duration, EarliestReceiptDate, EstimateActive, FOB, IncomeSubjectToTax, Invoiced, IsAdjustment, Item, ItemDesc, ItemVendor, Label, LastModifiedBy, LatestOrPriorState, Memo, ModifiedTime, Name, NameAccountNumber, NameAddress, NameCity, NameContact, NameEmail, NameFax, NamePhone, NameState, NameZip, OpenBalance, OriginalAmount, PaidAmount, PaidStatus, PaidThroughDate, PaymentMethod, PayrollItem, Percent, PercentChange, PercentOfTotalRetail, PercentOfTotalValue, PhysicalCount, PONumber, PrintStatus, ProgressAmount, ProgressPercent, Quantity, QuantityAvailable, QuantityOnHand, QuantityOnOrder, QuantityOnPendingBuild, QuantityOnSalesOrder, ReceivedQuantity, RefNumber, ReorderPoint, RetailValueOnHand, RunningBalance, SalesPerWeek, SalesRep, SalesTaxCode, ShipDate, ShipMethod, ShipToAddr1, ShipToAddr2, ShipToAddr3, ShipToAddr4, ShipToAddr5, SONumber, SourceName, SplitAccount, SSNOrTaxID, SuggestedReorder, TaxLine, TaxTableVersion, Terms, Total, TxnID, TxnNumber, TxnType, UnitPrice, UserEdit, ValueOnHand, WageBase, WageBaseTips -->
|
||||
<ColType >ENUMTYPE</ColType> <!-- required -->
|
||||
</ColDesc>
|
||||
<ReportData> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<DataRow> <!-- optional -->
|
||||
<RowData rowType="ENUMTYPE" value="STRTYPE"> <!-- optional -->
|
||||
</RowData>
|
||||
<ColData colID="INTTYPE" value="STRTYPE" dataType="ENUMTYPE"> <!-- optional, may repeat -->
|
||||
</ColData>
|
||||
</DataRow>
|
||||
<!-- OR -->
|
||||
<TextRow rowNumber="INTTYPE" value="STRTYPE"> <!-- optional -->
|
||||
</TextRow>
|
||||
<!-- OR -->
|
||||
<SubtotalRow> <!-- optional -->
|
||||
<RowData rowType="ENUMTYPE" value="STRTYPE"> <!-- optional -->
|
||||
</RowData>
|
||||
<ColData colID="INTTYPE" value="STRTYPE" dataType="ENUMTYPE"> <!-- optional, may repeat -->
|
||||
</ColData>
|
||||
</SubtotalRow>
|
||||
<!-- OR -->
|
||||
<TotalRow> <!-- optional -->
|
||||
<RowData rowType="ENUMTYPE" value="STRTYPE"> <!-- optional -->
|
||||
</RowData>
|
||||
<ColData colID="INTTYPE" value="STRTYPE" dataType="ENUMTYPE"> <!-- optional, may repeat -->
|
||||
</ColData>
|
||||
</TotalRow>
|
||||
<!-- END OR -->
|
||||
</ReportData>
|
||||
</ReportRet>
|
||||
</GeneralSummaryReportQueryRs>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
BIN
ItemInventory140723.xlsx
Normal file
BIN
ItemInventory140723.xlsx
Normal file
Binary file not shown.
199
ItemInventoryQuery.py
Normal file
199
ItemInventoryQuery.py
Normal file
@ -0,0 +1,199 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
import win32com.client
|
||||
import pandas as pd
|
||||
from datetime import date, datetime
|
||||
import timeit
|
||||
import os
|
||||
|
||||
class ItemInventoryQuery:
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
# print(f'kwargs:{kwargs}')
|
||||
# print(args)
|
||||
self.filename=None
|
||||
if len(args)>0:
|
||||
self.filename = args[0]
|
||||
# print(f'args:{args}')
|
||||
# print(self.filename)
|
||||
self.IncludeRetElement = kwargs['IncludeRetElement'] if 'IncludeRetElement' in kwargs else None
|
||||
if not isinstance(self.IncludeRetElement, list):
|
||||
self.IncludeRetElement = [self.IncludeRetElement]
|
||||
self.OwnerID = kwargs['OwnerID'] if 'OwnerID' in kwargs else "0"
|
||||
self.MaxReturned = kwargs['MaxReturned'] if 'MaxReturned' in kwargs else None
|
||||
if isinstance(self.MaxReturned, int):
|
||||
self.MaxReturned = str(self.MaxReturned)
|
||||
self.NameRangeFilter = kwargs['NameRangeFilter'] if 'NameRangeFilter' in kwargs else []
|
||||
self.DN = kwargs['DN'] if 'DN' in kwargs else {}
|
||||
self.TxnDateRangeFilter = kwargs['TxnDateRangeFilter'] if 'TxnDateRangeFilter' in kwargs else None
|
||||
self.DateMacro = None
|
||||
if 'DateMacro' in kwargs:
|
||||
if kwargs['DateMacro'] in ['All', 'Today', 'ThisWeek', 'ThisWeekToDate', 'ThisMonth', 'ThisMonthToDate', 'ThisQuarter', 'ThisQuarterToDate', 'ThisYear', 'ThisYearToDate', 'Yesterday', 'LastWeek', 'LastWeekToDate', 'LastMonth', 'LastMonthToDate', 'LastQuarter', 'LastQuarterToDate', 'LastYear', 'LastYearToDate', 'NextWeek', 'NextFourWeeks', 'NextMonth', 'NextQuarter', 'NextYear']:
|
||||
self.DateMacro = kwargs['DateMacro']
|
||||
self.FromTxnDate = self.validate_date(kwargs['FromTxnDate']) if 'FromTxnDate' in kwargs else None
|
||||
self.ToTxnDate = self.validate_date(kwargs['ToTxnDate']) if 'ToTxnDate' in kwargs else None
|
||||
self.ReportEntityFilter = kwargs['ReportEntityFilter'] if 'ReportEntityFilter' in kwargs else None
|
||||
self.FullName = kwargs['FullName'] if 'FullName' in kwargs else None
|
||||
# print(self.DateMacro, self.TxnDateRangeFilter, self.FromTxnDate, self.ToTxnDate)
|
||||
|
||||
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
|
||||
if type(attrib) is not dict:
|
||||
attrib = {}
|
||||
ele = ET.SubElement(parentNode, thisNode)
|
||||
for x in attrib:
|
||||
ele.set(x, attrib[x])
|
||||
ele.text = text
|
||||
tail = "\n"
|
||||
for x in range(whiteSpace):
|
||||
tail = tail + " "
|
||||
ele.tail = tail
|
||||
return ele
|
||||
|
||||
def create_QBXML(self):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
ItemInventoryQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "ItemInventoryQueryRq","\n " )
|
||||
|
||||
if self.MaxReturned is not None:
|
||||
MaxReturned = self.create_sub_element(ET, ItemInventoryQueryRq, "MaxReturned", self.MaxReturned, 4)
|
||||
|
||||
if len(self.NameRangeFilter)==2:
|
||||
print(self.NameRangeFilter)
|
||||
NameRangeFilter = self.create_sub_element(ET, ItemInventoryQueryRq, "NameRangeFilter", "\n ")
|
||||
FromName = self.create_sub_element(ET, NameRangeFilter, "FromName", self.NameRangeFilter[0])
|
||||
ToName = self.create_sub_element(ET, NameRangeFilter, "ToName", self.NameRangeFilter[1])
|
||||
|
||||
if self.IncludeRetElement:
|
||||
for x in self.IncludeRetElement:
|
||||
IncludeRetElement = self.create_sub_element(ET, ItemInventoryQueryRq, "IncludeRetElement", x, 4)
|
||||
# print(self.OwnerID)
|
||||
if self.OwnerID is not None:
|
||||
OwnerID = self.create_sub_element(ET, ItemInventoryQueryRq, "OwnerID", self.OwnerID, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_QBXML->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
def connect_to_quickbooks(self, qbxml_query):
|
||||
# Connect to Quickbooks
|
||||
# sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
# sessionManager.OpenConnection('', 'DASA')
|
||||
# enumfodnc= win32com.client.Dispatch('QBXMLRP2.RequestProcessor')
|
||||
# print(enumfodnc)
|
||||
# print(enumfodnc.qbFileOpenDoNotCare)
|
||||
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
sessionManager.OpenConnection('', 'DASA2')
|
||||
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
|
||||
|
||||
ticket = sessionManager.BeginSession("", 2)
|
||||
|
||||
# Send query and receive response
|
||||
response_string = sessionManager.ProcessRequest(ticket, qbxml_query)
|
||||
|
||||
# Disconnect from Quickbooks
|
||||
sessionManager.EndSession(ticket) # Close the company file
|
||||
sessionManager.CloseConnection() # Close the connection
|
||||
# print (f'response_string: {response_string}')
|
||||
return response_string
|
||||
|
||||
def status_ok(self, QBXML): #for ItemInventoryQueryRs
|
||||
tree = ET.fromstring(QBXML)
|
||||
GSRQRs = tree.find(".//ItemInventoryQueryRs")
|
||||
# print(f"GSRQRs:{GSRQRs}")
|
||||
status_code = GSRQRs.attrib #.get('statusCode')
|
||||
# print(GSRQRs.attrib)
|
||||
# print(GSRQRs.attrib['statusCode'])
|
||||
status=GSRQRs.attrib.get('statusMessage')
|
||||
|
||||
print(f'ItemInventoryQuery->status={status}')
|
||||
# print('OK' in status)
|
||||
if 'OK' in status:
|
||||
return True, status_code
|
||||
else:
|
||||
return False, status_code
|
||||
|
||||
def get_data(self, response_string):
|
||||
tree = ET.fromstring(response_string)
|
||||
data = tree.findall(".//ItemInventoryRet")
|
||||
NameFromTaco = []
|
||||
FullName = []
|
||||
# print(f'get_data->response_string:{response_string}')
|
||||
# print(data)
|
||||
for dt in data:
|
||||
# print(dt.find('FullName').text, )
|
||||
for dtextret in dt.findall('DataExtRet'):
|
||||
|
||||
if dtextret.findtext('DataExtName')=='NameFromTaco':
|
||||
FullName.append(dt.find('FullName').text.strip())
|
||||
NameFromTaco.append(dtextret.findtext('DataExtValue').strip())
|
||||
# print(f"{dt.find('FullName').text}->{dtextret.find('DataExtName').text} : {dtextret.findtext('DataExtValue')}")
|
||||
# print()
|
||||
if len(FullName)>0 and len(NameFromTaco)>0 and len(FullName) == len(NameFromTaco):
|
||||
# print(f'ItemInventoryQuery->exact len:{len(FullName)}')
|
||||
return {'FullName': FullName, 'NameFromTaco': NameFromTaco}
|
||||
else:
|
||||
print(f'ItemInventoryQuery->Not Exact Len:Fullname={len(FullName)}; NameFromTaco={len(NameFromTaco)}')
|
||||
return {}
|
||||
def to_excel(self, filename:str=''):
|
||||
start = timeit.default_timer()
|
||||
# print(f'to_excel filename:{self.filename}')
|
||||
if filename == '':
|
||||
print("filename is none")
|
||||
if self.filename != None:
|
||||
print("self.filename not none")
|
||||
filename = self.filename
|
||||
else:
|
||||
return False, 'Please fill excel filename.'
|
||||
# print(filename, type(filename))
|
||||
if not filename.endswith('.xlsx'):
|
||||
return False, 'filename has to be excel file(.xlsx)'
|
||||
# print(self.create_QBXML())
|
||||
response_string = self.connect_to_quickbooks(self.create_QBXML())
|
||||
|
||||
ret, msg = self.status_ok(response_string)
|
||||
if ret:
|
||||
df = pd.DataFrame.from_dict(self.get_data(response_string))
|
||||
print(df)
|
||||
if len(df)>0:
|
||||
df.to_excel(filename, index=False)
|
||||
modtime = datetime.fromtimestamp(os.path.getmtime(filename))
|
||||
print(f"modified Time:{modtime}")
|
||||
else:
|
||||
False, 'There is no data(df==0)'
|
||||
else:
|
||||
return False, msg
|
||||
print("The difference of time is :", timeit.default_timer() - start)
|
||||
return True, f"It takes {format(timeit.default_timer() - start, '.2f')} seconds to update {filename}"
|
||||
|
||||
start = timeit.default_timer()
|
||||
if __name__ == "__main__":
|
||||
# ini= ItemInventoryQuery(IncludeRetElement='FullName', OwnerID = "0")
|
||||
for x in range(0, 1):
|
||||
# ini= ItemInventoryQuery(IncludeRetElement=['FullName', 'DataExtRet'] , OwnerID = str(x), MaxReturned=None)
|
||||
# ini= ItemInventoryQuery( OwnerID = str(x), MaxReturned="10", NameRangeFilter=["TSHT:TS WOODGRAIN:TS-252", "TSHT:TS WOODGRAIN:TS-252"])
|
||||
# ini= ItemInventoryQuery( OwnerID = str(x), MaxReturned="10", NameRangeFilter=["TSHT:TS LUXURY:TS-L-252", "TSHT:TS LUXURY:TS-L-252"])
|
||||
ini= ItemInventoryQuery( OwnerID = str(x), MaxReturned="10")
|
||||
# ini= ItemInventoryQuery( OwnerID = str(x))
|
||||
# ini.to_excel('ItemInventory\ItemInventory_FromQB.xlsx')
|
||||
itu = ini.connect_to_quickbooks(ini.create_QBXML())
|
||||
# print(itu)
|
||||
ret, msg = ini.status_ok(itu)
|
||||
if ret:
|
||||
print("YEAH")
|
||||
df = pd.DataFrame.from_dict(ini.get_data(itu))
|
||||
print(df)
|
||||
# df.to_excel('ItemInventory\ItemInventory_FromQB.xlsx', index=False)
|
||||
modtime = datetime.fromtimestamp(os.path.getmtime('ItemInventory\ItemInventory_FromQB.xlsx'))
|
||||
print(f"modified Time:{modtime}")
|
||||
break
|
||||
# print(ini.status_ok(itu))
|
||||
# print(ini.create_QBXML())
|
||||
print("The difference of time is :",
|
||||
timeit.default_timer() - start)
|
||||
114
ItemIventoryQuery.xml
Normal file
114
ItemIventoryQuery.xml
Normal file
@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?qbxml version="16.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<ItemInventoryQueryRq metaData="ENUMTYPE" iterator="ENUMTYPE" iteratorID="UUIDTYPE">
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<MaxReturned >INTTYPE</MaxReturned> <!-- optional -->
|
||||
<!-- ActiveStatus may have one of the following values: ActiveOnly [DEFAULT], InactiveOnly, All -->
|
||||
<ActiveStatus >ENUMTYPE</ActiveStatus> <!-- optional -->
|
||||
<FromModifiedDate >DATETIMETYPE</FromModifiedDate> <!-- optional -->
|
||||
<ToModifiedDate >DATETIMETYPE</ToModifiedDate> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<NameFilter> <!-- optional -->
|
||||
<!-- MatchCriterion may have one of the following values: StartsWith, Contains, EndsWith -->
|
||||
<MatchCriterion >ENUMTYPE</MatchCriterion> <!-- required -->
|
||||
<Name >STRTYPE</Name> <!-- required -->
|
||||
</NameFilter>
|
||||
<!-- OR -->
|
||||
<NameRangeFilter> <!-- optional -->
|
||||
<FromName >STRTYPE</FromName> <!-- optional -->
|
||||
<ToName >STRTYPE</ToName> <!-- optional -->
|
||||
</NameRangeFilter>
|
||||
<!-- END OR -->
|
||||
<ClassFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</ClassFilter>
|
||||
<!-- END OR -->
|
||||
<IncludeRetElement >STRTYPE</IncludeRetElement> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional, may repeat -->
|
||||
</ItemInventoryQueryRq>
|
||||
|
||||
<ItemInventoryQueryRs statusCode="INTTYPE" statusSeverity="STRTYPE" statusMessage="STRTYPE" retCount="INTTYPE" iteratorRemainingCount="INTTYPE" iteratorID="UUIDTYPE">
|
||||
<ItemInventoryRet> <!-- optional, may repeat -->
|
||||
<ListID >IDTYPE</ListID> <!-- required -->
|
||||
<TimeCreated >DATETIMETYPE</TimeCreated> <!-- required -->
|
||||
<TimeModified >DATETIMETYPE</TimeModified> <!-- required -->
|
||||
<EditSequence >STRTYPE</EditSequence> <!-- required -->
|
||||
<Name >STRTYPE</Name> <!-- required -->
|
||||
<FullName >STRTYPE</FullName> <!-- required -->
|
||||
<BarCodeValue >STRTYPE</BarCodeValue> <!-- optional -->
|
||||
<IsActive >BOOLTYPE</IsActive> <!-- optional -->
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<ParentRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ParentRef>
|
||||
<Sublevel >INTTYPE</Sublevel> <!-- required -->
|
||||
<ManufacturerPartNumber >STRTYPE</ManufacturerPartNumber> <!-- optional -->
|
||||
<UnitOfMeasureSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</UnitOfMeasureSetRef>
|
||||
<IsTaxIncluded >BOOLTYPE</IsTaxIncluded> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<SalesDesc >STRTYPE</SalesDesc> <!-- optional -->
|
||||
<SalesPrice >PRICETYPE</SalesPrice> <!-- optional -->
|
||||
<IncomeAccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</IncomeAccountRef>
|
||||
<PurchaseDesc >STRTYPE</PurchaseDesc> <!-- optional -->
|
||||
<PurchaseCost >PRICETYPE</PurchaseCost> <!-- optional -->
|
||||
<PurchaseTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</PurchaseTaxCodeRef>
|
||||
<COGSAccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</COGSAccountRef>
|
||||
<PrefVendorRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</PrefVendorRef>
|
||||
<AssetAccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</AssetAccountRef>
|
||||
<ReorderPoint >QUANTYPE</ReorderPoint> <!-- optional -->
|
||||
<Max >QUANTYPE</Max> <!-- optional -->
|
||||
<QuantityOnHand >QUANTYPE</QuantityOnHand> <!-- optional -->
|
||||
<AverageCost >PRICETYPE</AverageCost> <!-- optional -->
|
||||
<QuantityOnOrder >QUANTYPE</QuantityOnOrder> <!-- optional -->
|
||||
<QuantityOnSalesOrder >QUANTYPE</QuantityOnSalesOrder> <!-- optional -->
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</ItemInventoryRet>
|
||||
</ItemInventoryQueryRs>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
5
ItemReceipt.ini
Normal file
5
ItemReceipt.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[Section 1]
|
||||
key1 = myval1
|
||||
key2 = value2
|
||||
#item_inventory_path = ItemInventory\ItemInventory140723.xlsx
|
||||
|
||||
272
ItemReceiptXml.xml
Normal file
272
ItemReceiptXml.xml
Normal file
@ -0,0 +1,272 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?qbxml version="16.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<ItemReceiptAddRq>
|
||||
<ItemReceiptAdd defMacro="MACROTYPE"> <!-- required -->
|
||||
<VendorRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</VendorRef>
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- optional -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<Memo >STRTYPE</Memo> <!-- optional -->
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
<LinkToTxnID >IDTYPE</LinkToTxnID> <!-- optional, may repeat -->
|
||||
<ItemLineAdd> <!-- optional -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<Cost >PRICETYPE</Cost> <!-- optional -->
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<LinkToTxn> <!-- optional -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
</LinkToTxn>
|
||||
</ItemLineAdd>
|
||||
</ItemReceiptAdd>
|
||||
<IncludeRetElement >STRTYPE</IncludeRetElement> <!-- optional, may repeat -->
|
||||
</ItemReceiptAddRq>
|
||||
|
||||
<ItemReceiptAddRs statusCode="INTTYPE" statusSeverity="STRTYPE" statusMessage="STRTYPE">
|
||||
<ItemReceiptRet> <!-- optional -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<TimeCreated >DATETIMETYPE</TimeCreated> <!-- required -->
|
||||
<TimeModified >DATETIMETYPE</TimeModified> <!-- required -->
|
||||
<EditSequence >STRTYPE</EditSequence> <!-- required -->
|
||||
<TxnNumber >INTTYPE</TxnNumber> <!-- optional -->
|
||||
<VendorRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</VendorRef>
|
||||
<!-- BEGIN OR -->
|
||||
<APAccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</APAccountRef>
|
||||
<!-- OR -->
|
||||
<LiabilityAccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</LiabilityAccountRef>
|
||||
<!-- END OR -->
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- required -->
|
||||
<TotalAmount >AMTTYPE</TotalAmount> <!-- required -->
|
||||
<CurrencyRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CurrencyRef>
|
||||
<ExchangeRate >FLOATTYPE</ExchangeRate> <!-- optional -->
|
||||
<TotalAmountInHomeCurrency >AMTTYPE</TotalAmountInHomeCurrency> <!-- optional -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<Memo >STRTYPE</Memo> <!-- optional -->
|
||||
<IsTaxIncluded >BOOLTYPE</IsTaxIncluded> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
<LinkedTxn> <!-- optional, may repeat -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<!-- TxnType may have one of the following values: ARRefundCreditCard, Bill, BillPaymentCheck, BillPaymentCreditCard, BuildAssembly, Charge, Check, CreditCardCharge, CreditCardCredit, CreditMemo, Deposit, Estimate, InventoryAdjustment, Invoice, ItemReceipt, JournalEntry, LiabilityAdjustment, Paycheck, PayrollLiabilityCheck, PurchaseOrder, ReceivePayment, SalesOrder, SalesReceipt, SalesTaxPaymentCheck, Transfer, VendorCredit, YTDAdjustment -->
|
||||
<TxnType >ENUMTYPE</TxnType> <!-- required -->
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<!-- LinkType may have one of the following values: AMTTYPE, QUANTYPE -->
|
||||
<LinkType >ENUMTYPE</LinkType> <!-- optional -->
|
||||
<Amount >AMTTYPE</Amount> <!-- required -->
|
||||
</LinkedTxn>
|
||||
<ExpenseLineRet> <!-- optional, may repeat -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<AccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</AccountRef>
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<Memo >STRTYPE</Memo> <!-- optional -->
|
||||
<CustomerRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerRef>
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<!-- BillableStatus may have one of the following values: Billable, NotBillable, HasBeenBilled -->
|
||||
<BillableStatus >ENUMTYPE</BillableStatus> <!-- optional -->
|
||||
<SalesRepRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesRepRef>
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</ExpenseLineRet>
|
||||
<!-- BEGIN OR -->
|
||||
<ItemLineRet> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ExpirationDateForSerialLotNumber >STRTYPE</ExpirationDateForSerialLotNumber> <!-- optional -->
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<Cost >PRICETYPE</Cost> <!-- optional -->
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<CustomerRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerRef>
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<!-- BillableStatus may have one of the following values: Billable, NotBillable, HasBeenBilled -->
|
||||
<BillableStatus >ENUMTYPE</BillableStatus> <!-- optional -->
|
||||
<SalesRepRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesRepRef>
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</ItemLineRet>
|
||||
<!-- OR -->
|
||||
<ItemGroupLineRet> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemGroupRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemGroupRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<TotalAmount >AMTTYPE</TotalAmount> <!-- required -->
|
||||
<ItemLineRet> <!-- optional, may repeat -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ExpirationDateForSerialLotNumber >STRTYPE</ExpirationDateForSerialLotNumber> <!-- optional -->
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<Cost >PRICETYPE</Cost> <!-- optional -->
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<CustomerRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerRef>
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<!-- BillableStatus may have one of the following values: Billable, NotBillable, HasBeenBilled -->
|
||||
<BillableStatus >ENUMTYPE</BillableStatus> <!-- optional -->
|
||||
<SalesRepRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesRepRef>
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</ItemLineRet>
|
||||
<DataExt> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- required -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExt>
|
||||
</ItemGroupLineRet>
|
||||
<!-- END OR -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</ItemReceiptRet>
|
||||
<ErrorRecovery> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<TxnNumber >INTTYPE</TxnNumber> <!-- optional -->
|
||||
<EditSequence >STRTYPE</EditSequence> <!-- optional -->
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
</ErrorRecovery>
|
||||
</ItemReceiptAddRs>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
146
PriceLevelQuery.py
Normal file
146
PriceLevelQuery.py
Normal file
@ -0,0 +1,146 @@
|
||||
#!usr/bin/python
|
||||
import win32com.client
|
||||
import xml.etree.ElementTree as ET
|
||||
import json
|
||||
import xmltodict
|
||||
|
||||
# Connect to Quickbooks
|
||||
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
sessionManager.OpenConnection('', 'Test qbXML Request')
|
||||
ticket = sessionManager.BeginSession("C:\Quickbooks Bogor\qb test\distrindo bakti wutama.qbw", 0)
|
||||
|
||||
# Send query and receive response
|
||||
qbxml_query = """
|
||||
<?qbxml version="13.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<ItemInventoryQueryRq metaData="MetaDataAndResponseData">
|
||||
<NameFilter>
|
||||
<MatchCriterion>Contains</MatchCriterion>
|
||||
<Name>TS-I502</Name>
|
||||
</NameFilter>
|
||||
<IncludeRetElement>QuantityOnHand</IncludeRetElement>
|
||||
</ItemInventoryQueryRq>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
"""
|
||||
qbxml_query = """
|
||||
<?qbxml version="13.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<PriceLevelQueryRq metaData="MetaDataAndResponseData">
|
||||
<FullName>A</FullName>
|
||||
<FullName>B</FullName>
|
||||
|
||||
</PriceLevelQueryRq>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
"""
|
||||
qbxml_query = """<?qbxml version="13.0"?>
|
||||
<QBXML><QBXMLMsgsRq onError="stopOnError"><PriceLevelQueryRq metaData="MetaDataAndResponseData"><FullName>A</FullName><FullName>B</FullName></PriceLevelQueryRq></QBXMLMsgsRq></QBXML>"""
|
||||
|
||||
qbxml_query1 = """
|
||||
<?qbxml version="13.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<ItemInventoryQueryRq metaData="MetaDataAndResponseData">
|
||||
<IncludeRetElement>QuantityOnHand</IncludeRetElement>
|
||||
<IncludeRetElement>QuantityOnOrder</IncludeRetElement>
|
||||
<IncludeRetElement>QuantityOnSalesOrder</IncludeRetElement>
|
||||
<IncludeRetElement>FullName</IncludeRetElement>
|
||||
<IncludeRetElement>Name</IncludeRetElement>
|
||||
</ItemInventoryQueryRq>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
"""
|
||||
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n "
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
PriceLevelQueryRq = ET.SubElement(QBXMLMsgsRq, "PriceLevelQueryRq")
|
||||
#PriceLevelQueryRq.set("metaData", "MetaDataAndResponseData")
|
||||
PriceLevelQueryRq.set("metaData", "NoMetaData")
|
||||
PriceLevelQueryRq.tail = "\n "
|
||||
PriceLevelQueryRq.text = "\n "
|
||||
FullName = ET.SubElement(PriceLevelQueryRq, "FullName")
|
||||
FullName.text = "A"
|
||||
FullName.tail = "\n "
|
||||
FullName = ET.SubElement(PriceLevelQueryRq, "FullName")
|
||||
FullName.text = "C"
|
||||
FullName.tail = "\n "
|
||||
|
||||
#mydata = ET.tostring(root, encoding = "unicode", method = "xml")
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
#mydata = ET.tostring(root).decode()
|
||||
print (mydata,type(mydata))
|
||||
qbxml_query1 = """<?xml version="1.0" encoding="utf-8"?>"""
|
||||
qbxml_query1 = qbxml_query1 + """<?qbxml version="11.0"?>"""
|
||||
qbxml_query1 = qbxml_query1 + "\n" + mydata
|
||||
print("")
|
||||
print(qbxml_query1, type(qbxml_query1))
|
||||
print("")
|
||||
#print(qbxml_query, type(qbxml_query))
|
||||
|
||||
|
||||
|
||||
tree = ET.ElementTree(root)
|
||||
print("test",tree)
|
||||
|
||||
#response_string = sessionManager.ProcessRequest(ticket, qbxml_query1)
|
||||
|
||||
# Disconnect from Quickbooks
|
||||
sessionManager.EndSession(ticket) # Close the company file
|
||||
sessionManager.CloseConnection() # Close the connection
|
||||
#print (response_string)
|
||||
# Parse the response into an Element Tree and peel away the layers of response
|
||||
#QBXML = xml.etree.ElementTree.fromstring(response_string)
|
||||
QBXML = ET.fromstring(response_string)
|
||||
print("")
|
||||
#print(QBXML)
|
||||
#print(QBXML)
|
||||
datadict=xmltodict.parse(response_string)
|
||||
#print(datadict)
|
||||
jsondata=json.dumps(datadict)
|
||||
#print(jsondata)
|
||||
ddict=json.loads(jsondata)
|
||||
#print(ddict)
|
||||
QBXMLMsgsRs = QBXML.find('QBXMLMsgsRs')
|
||||
#InventoryAdjustmentQueryRs = QBXMLMsgsRs.getiterator("InventoryAdjustmentRet")
|
||||
#InventoryAdjustmentQueryRs = QBXMLMsgsRs.iter("ItemInventoryRet")
|
||||
PriceLevelQueryRs = QBXMLMsgsRs.iter("PriceLevelRet")
|
||||
#print (PriceLevelQueryRs)
|
||||
PriceLevelRet = ddict["QBXML"]["QBXMLMsgsRs"]["PriceLevelQueryRs"]["PriceLevelRet"]
|
||||
|
||||
#print (priceLevelPerItemRet)
|
||||
|
||||
for priceLevelPerItemRetList in PriceLevelRet:
|
||||
#print(priceLevelPerItemRetList)
|
||||
#priceLevelPerItemRet = priceLevelPerItemRet["PriceLevelPerItemRet"]
|
||||
|
||||
for PriceLevelPerItemRet in priceLevelPerItemRetList["PriceLevelPerItemRet"]:
|
||||
pass
|
||||
print(priceLevelPerItemRetList["Name"],PriceLevelPerItemRet["ItemRef"]["FullName"], PriceLevelPerItemRet["CustomPrice"])
|
||||
|
||||
|
||||
|
||||
# for PriceLevelRet in PriceLevelQueryRs:
|
||||
# print(PriceLevelRet)
|
||||
# # try:
|
||||
# PLT = PriceLevelRet.find('PriceLevelType').text
|
||||
# IsActive = PriceLevelRet.find('IsActive').text
|
||||
# #txnid = PriceLevelRet.find('ListID').text
|
||||
# name = PriceLevelRet.find('Name').text
|
||||
# PriceLevelPerItemRet = PriceLevelRet.iter("PriceLevelPerItemRet")
|
||||
# for ItemRef in PriceLevelPerItemRet:
|
||||
# print(ItemRef)
|
||||
# FullName = ItemRef.find('FullName').text
|
||||
# CustomPrice = PriceLevelRet.find('CustomPrice').text
|
||||
# print(FullName, name, PLT, CustomPrice)
|
||||
# # except AttributeError:
|
||||
# # print("attrib err")
|
||||
# # except:
|
||||
# # print("err")
|
||||
147
PriceLevelQueryv2.py
Normal file
147
PriceLevelQueryv2.py
Normal file
@ -0,0 +1,147 @@
|
||||
#!usr/bin/python
|
||||
import win32com.client
|
||||
import xml.etree.ElementTree as ET
|
||||
import json
|
||||
import xmltodict
|
||||
|
||||
# Connect to Quickbooks
|
||||
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
sessionManager.OpenConnection('', 'Test qbXML Request')
|
||||
#ticket = sessionManager.BeginSession("C:\Quickbooks Bogor\qb test\distrindo bakti wutama.qbw", 0)
|
||||
ticket = sessionManager.BeginSession("", 0)
|
||||
|
||||
# Send query and receive response
|
||||
qbxml_query = """
|
||||
<?qbxml version="13.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<ItemInventoryQueryRq metaData="MetaDataAndResponseData">
|
||||
<NameFilter>
|
||||
<MatchCriterion>Contains</MatchCriterion>
|
||||
<Name>TS-I502</Name>
|
||||
</NameFilter>
|
||||
<IncludeRetElement>QuantityOnHand</IncludeRetElement>
|
||||
</ItemInventoryQueryRq>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
"""
|
||||
qbxml_query = """
|
||||
<?qbxml version="13.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<PriceLevelQueryRq metaData="MetaDataAndResponseData">
|
||||
<FullName>A</FullName>
|
||||
<FullName>B</FullName>
|
||||
|
||||
</PriceLevelQueryRq>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
"""
|
||||
qbxml_query = """<?qbxml version="13.0"?>
|
||||
<QBXML><QBXMLMsgsRq onError="stopOnError"><PriceLevelQueryRq metaData="MetaDataAndResponseData"><FullName>A</FullName><FullName>B</FullName></PriceLevelQueryRq></QBXMLMsgsRq></QBXML>"""
|
||||
|
||||
qbxml_query1 = """
|
||||
<?qbxml version="13.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<ItemInventoryQueryRq metaData="MetaDataAndResponseData">
|
||||
<IncludeRetElement>QuantityOnHand</IncludeRetElement>
|
||||
<IncludeRetElement>QuantityOnOrder</IncludeRetElement>
|
||||
<IncludeRetElement>QuantityOnSalesOrder</IncludeRetElement>
|
||||
<IncludeRetElement>FullName</IncludeRetElement>
|
||||
<IncludeRetElement>Name</IncludeRetElement>
|
||||
</ItemInventoryQueryRq>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
"""
|
||||
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n "
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
PriceLevelQueryRq = ET.SubElement(QBXMLMsgsRq, "PriceLevelQueryRq")
|
||||
#PriceLevelQueryRq.set("metaData", "MetaDataAndResponseData")
|
||||
PriceLevelQueryRq.set("metaData", "NoMetaData")
|
||||
PriceLevelQueryRq.tail = "\n "
|
||||
PriceLevelQueryRq.text = "\n "
|
||||
FullName = ET.SubElement(PriceLevelQueryRq, "FullName")
|
||||
FullName.text = "A"
|
||||
FullName.tail = "\n "
|
||||
FullName = ET.SubElement(PriceLevelQueryRq, "FullName")
|
||||
FullName.text = "C"
|
||||
FullName.tail = "\n "
|
||||
|
||||
#mydata = ET.tostring(root, encoding = "unicode", method = "xml")
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
#mydata = ET.tostring(root).decode()
|
||||
print (mydata,type(mydata))
|
||||
qbxml_query1 = """<?xml version="1.0" encoding="utf-8"?>"""
|
||||
qbxml_query1 = qbxml_query1 + """<?qbxml version="11.0"?>"""
|
||||
qbxml_query1 = qbxml_query1 + "\n" + mydata
|
||||
print("")
|
||||
print(qbxml_query1, type(qbxml_query1))
|
||||
print("")
|
||||
#print(qbxml_query, type(qbxml_query))
|
||||
|
||||
|
||||
|
||||
tree = ET.ElementTree(root)
|
||||
print("test",tree)
|
||||
|
||||
response_string = sessionManager.ProcessRequest(ticket, qbxml_query1)
|
||||
|
||||
# Disconnect from Quickbooks
|
||||
sessionManager.EndSession(ticket) # Close the company file
|
||||
sessionManager.CloseConnection() # Close the connection
|
||||
print (response_string)
|
||||
# Parse the response into an Element Tree and peel away the layers of response
|
||||
#QBXML = xml.etree.ElementTree.fromstring(response_string)
|
||||
QBXML = ET.fromstring(response_string)
|
||||
print("")
|
||||
#print(QBXML)
|
||||
#print(QBXML)
|
||||
datadict=xmltodict.parse(response_string)
|
||||
#print(datadict)
|
||||
jsondata=json.dumps(datadict)
|
||||
#print(jsondata)
|
||||
ddict=json.loads(jsondata)
|
||||
#print(ddict)
|
||||
QBXMLMsgsRs = QBXML.find('QBXMLMsgsRs')
|
||||
#InventoryAdjustmentQueryRs = QBXMLMsgsRs.getiterator("InventoryAdjustmentRet")
|
||||
#InventoryAdjustmentQueryRs = QBXMLMsgsRs.iter("ItemInventoryRet")
|
||||
PriceLevelQueryRs = QBXMLMsgsRs.iter("PriceLevelRet")
|
||||
#print (PriceLevelQueryRs)
|
||||
PriceLevelRet = ddict["QBXML"]["QBXMLMsgsRs"]["PriceLevelQueryRs"]["PriceLevelRet"]
|
||||
|
||||
#print (priceLevelPerItemRet)
|
||||
|
||||
for priceLevelPerItemRetList in PriceLevelRet:
|
||||
#print(priceLevelPerItemRetList)
|
||||
#priceLevelPerItemRet = priceLevelPerItemRet["PriceLevelPerItemRet"]
|
||||
|
||||
for PriceLevelPerItemRet in priceLevelPerItemRetList["PriceLevelPerItemRet"]:
|
||||
pass
|
||||
print(priceLevelPerItemRetList["Name"],PriceLevelPerItemRet["ItemRef"]["FullName"], PriceLevelPerItemRet["CustomPrice"])
|
||||
|
||||
|
||||
|
||||
# for PriceLevelRet in PriceLevelQueryRs:
|
||||
# print(PriceLevelRet)
|
||||
# # try:
|
||||
# PLT = PriceLevelRet.find('PriceLevelType').text
|
||||
# IsActive = PriceLevelRet.find('IsActive').text
|
||||
# #txnid = PriceLevelRet.find('ListID').text
|
||||
# name = PriceLevelRet.find('Name').text
|
||||
# PriceLevelPerItemRet = PriceLevelRet.iter("PriceLevelPerItemRet")
|
||||
# for ItemRef in PriceLevelPerItemRet:
|
||||
# print(ItemRef)
|
||||
# FullName = ItemRef.find('FullName').text
|
||||
# CustomPrice = PriceLevelRet.find('CustomPrice').text
|
||||
# print(FullName, name, PLT, CustomPrice)
|
||||
# # except AttributeError:
|
||||
# # print("attrib err")
|
||||
# # except:
|
||||
# # print("err")
|
||||
144
SO_to_Inv/CustomerQuery.py
Normal file
144
SO_to_Inv/CustomerQuery.py
Normal file
@ -0,0 +1,144 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
import win32com.client
|
||||
import xmltodict
|
||||
import pprint
|
||||
import datetime
|
||||
import pandas as pd
|
||||
from datetime import date
|
||||
import timeit
|
||||
import os
|
||||
|
||||
class CustomerQuery:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
# print(f'kwargs:{kwargs}')
|
||||
# print(args)
|
||||
self.CustomerPriceLevelNames = []
|
||||
self.SPPriceLevelNames = []
|
||||
self.CustomerFullNames = []
|
||||
self.item_inventory_path = "ItemInventory"
|
||||
self._filename = "CustomerList.xlsx"
|
||||
# self._df_customer = pd.read_excel(os.path.join(os.getcwd(), self.item_inventory_path, self._filename), usecols=['FullName', 'PriceLevelName', 'Special Cust'],)
|
||||
# print(self._df_customer)
|
||||
# print(type(self._df_customer.loc[(self._df_customer['FullName']=="ECO:0:ECO-002") & (self._df_customer['PriceLevelName']=="T 202202")].values.tolist()[0][2]))
|
||||
self.IncludeRetElement = kwargs['IncludeRetElement'] if 'IncludeRetElement' in kwargs else []
|
||||
self.TxnDateRangeFilter = kwargs['TxnDateRangeFilter'] if 'TxnDateRangeFilter' in kwargs else None
|
||||
self.FullName = kwargs['FullName'] if 'FullName' in kwargs else None
|
||||
|
||||
def get_customer_pricelevel_list(self, response_string):
|
||||
QBXML = ET.fromstring(response_string)
|
||||
CustomerRets = QBXML.findall('.//CustomerRet')
|
||||
# print(f'CustomerRets:{CustomerRets}')
|
||||
|
||||
for CustomerRet in CustomerRets:
|
||||
CustomerFullName = None
|
||||
PriceLevelName = None
|
||||
SP_PriceLevelName = None
|
||||
CustomerFullName = CustomerRet.find('FullName').text
|
||||
PriceLevelName = CustomerRet.find('.//PriceLevelRef')
|
||||
if PriceLevelName:
|
||||
PriceLevelName = PriceLevelName.find("FullName").text
|
||||
# print(f'PriceLevelName:{PriceLevelName}')
|
||||
DataExtRets = CustomerRet.findall('.//DataExtRet')
|
||||
SP_PriceLevelName = None
|
||||
if len(DataExtRets)>0:
|
||||
for DataExtRet in DataExtRets:
|
||||
DataExtName = DataExtRet.find('DataExtName').text
|
||||
DataExtValue = DataExtRet.find('DataExtValue').text
|
||||
if DataExtName.lower() == 'special cust'.lower():
|
||||
SP_PriceLevelName = DataExtValue.upper()
|
||||
break
|
||||
self.CustomerPriceLevelNames.append(PriceLevelName)
|
||||
self.SPPriceLevelNames.append(SP_PriceLevelName)
|
||||
self.CustomerFullNames.append(CustomerFullName)
|
||||
# print(self.CustomerFullNames, self.CustomerPriceLevelNames, self.SPPriceLevelNames)
|
||||
Customer = {}
|
||||
Customer['FullName']=self.CustomerFullNames
|
||||
Customer['PriceLevelName']=self.CustomerPriceLevelNames
|
||||
Customer['SPName']= self.SPPriceLevelNames
|
||||
# print(Customer)
|
||||
_df = pd.DataFrame.from_dict(Customer)
|
||||
print(_df)
|
||||
_df.to_excel(os.path.join(os.getcwd(), self.item_inventory_path, self._filename), index=False)
|
||||
return PriceLevelName, SP_PriceLevelName
|
||||
|
||||
def create_customerquery_QBXML(self ):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
CustomerQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "CustomerQueryRq","\n " )
|
||||
# FullName = self.create_sub_element(ET, CustomerQueryRq, "FullName", self.FullName, 6)
|
||||
IncludeRetElement = ['FullName', 'PriceLevelRef', 'DataExtRet']
|
||||
for x in IncludeRetElement:
|
||||
IncludeRetElement = self.create_sub_element(ET, CustomerQueryRq, "IncludeRetElement", x, 4)
|
||||
OwnerID = self.create_sub_element(ET, CustomerQueryRq, "OwnerID", "0", 6)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_customer_QBXML->qbxml_query: {qbxml_query}')
|
||||
response_string=self.connect_to_quickbooks(qbxml_query)
|
||||
|
||||
return response_string
|
||||
|
||||
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
|
||||
if type(attrib) is not dict:
|
||||
attrib = {}
|
||||
ele = ET.SubElement(parentNode, thisNode)
|
||||
for x in attrib:
|
||||
ele.set(x, attrib[x])
|
||||
ele.text = text
|
||||
tail = "\n"
|
||||
for x in range(whiteSpace):
|
||||
tail = tail + " "
|
||||
ele.tail = tail
|
||||
return ele
|
||||
|
||||
def connect_to_quickbooks(self, qbxml_query):
|
||||
# Connect to Quickbooks
|
||||
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
sessionManager.OpenConnection('', 'DASA2')
|
||||
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
|
||||
ticket = sessionManager.BeginSession("", 2)
|
||||
|
||||
# Send query and receive response
|
||||
response_string = sessionManager.ProcessRequest(ticket, qbxml_query)
|
||||
|
||||
# Disconnect from Quickbooks
|
||||
sessionManager.EndSession(ticket) # Close the company file
|
||||
sessionManager.CloseConnection() # Close the connection
|
||||
# print (f'response_string:{response_string}')
|
||||
return response_string
|
||||
|
||||
def status_ok(self, QBXML): #for CustomerRs
|
||||
|
||||
tree = ET.fromstring(QBXML)
|
||||
|
||||
GSRQRs = tree.find(".//CustomerRs")
|
||||
# print(f"GSRQRs:{GSRQRs}")
|
||||
status_code = GSRQRs.attrib #.get('statusCode')
|
||||
# print(GSRQRs.attrib)
|
||||
# print(GSRQRs.attrib['statusCode'])
|
||||
status=GSRQRs.attrib.get('statusMessage')
|
||||
|
||||
print(f'status={status}')
|
||||
if 'OK' in status:
|
||||
return True, status_code
|
||||
else:
|
||||
return False, status_code
|
||||
|
||||
if __name__ == '__main__':
|
||||
starttime = timeit.default_timer()
|
||||
# ini=SalesOrderQuery(FullName= '999 HPL', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'IsManuallyClosed', 'IsFullyInvoiced'])
|
||||
|
||||
ini=CustomerQuery()
|
||||
iya = ini.create_customerquery_QBXML()
|
||||
# print(iya)
|
||||
ini.get_customer_pricelevel_list(iya)
|
||||
|
||||
print("The time difference is :", timeit.default_timer() - starttime)
|
||||
454
SO_to_Inv/InvoiceAdd.xml
Normal file
454
SO_to_Inv/InvoiceAdd.xml
Normal file
@ -0,0 +1,454 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?qbxml version="16.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<InvoiceAddRq>
|
||||
<InvoiceAdd defMacro="MACROTYPE"> <!-- required -->
|
||||
<CustomerRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerRef>
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<ARAccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ARAccountRef>
|
||||
<TemplateRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</TemplateRef>
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- optional -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<BillAddress> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
<City >STRTYPE</City> <!-- optional -->
|
||||
<State >STRTYPE</State> <!-- optional -->
|
||||
<PostalCode >STRTYPE</PostalCode> <!-- optional -->
|
||||
<Country >STRTYPE</Country> <!-- optional -->
|
||||
<Note >STRTYPE</Note> <!-- optional -->
|
||||
</BillAddress>
|
||||
<ShipAddress> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
<City >STRTYPE</City> <!-- optional -->
|
||||
<State >STRTYPE</State> <!-- optional -->
|
||||
<PostalCode >STRTYPE</PostalCode> <!-- optional -->
|
||||
<Country >STRTYPE</Country> <!-- optional -->
|
||||
<Note >STRTYPE</Note> <!-- optional -->
|
||||
</ShipAddress>
|
||||
<IsPending >BOOLTYPE</IsPending> <!-- optional -->
|
||||
<IsFinanceCharge >BOOLTYPE</IsFinanceCharge> <!-- optional -->
|
||||
<PONumber >STRTYPE</PONumber> <!-- optional -->
|
||||
<TermsRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</TermsRef>
|
||||
<DueDate >DATETYPE</DueDate> <!-- optional -->
|
||||
<SalesRepRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesRepRef>
|
||||
<FOB >STRTYPE</FOB> <!-- optional -->
|
||||
<ShipDate >DATETYPE</ShipDate> <!-- optional -->
|
||||
<ShipMethodRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ShipMethodRef>
|
||||
<ItemSalesTaxRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemSalesTaxRef>
|
||||
<Memo >STRTYPE</Memo> <!-- optional -->
|
||||
<CustomerMsgRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerMsgRef>
|
||||
<IsToBePrinted >BOOLTYPE</IsToBePrinted> <!-- optional -->
|
||||
<IsToBeEmailed >BOOLTYPE</IsToBeEmailed> <!-- optional -->
|
||||
<IsTaxIncluded >BOOLTYPE</IsTaxIncluded> <!-- optional -->
|
||||
<CustomerSalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerSalesTaxCodeRef>
|
||||
<Other >STRTYPE</Other> <!-- optional -->
|
||||
<ExchangeRate >FLOATTYPE</ExchangeRate> <!-- optional -->
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
<LinkToTxnID >IDTYPE</LinkToTxnID> <!-- optional, may repeat -->
|
||||
<SetCredit> <!-- optional, may repeat -->
|
||||
<CreditTxnID useMacro="MACROTYPE">IDTYPE</CreditTxnID> <!-- required -->
|
||||
<AppliedAmount >AMTTYPE</AppliedAmount> <!-- required -->
|
||||
<Override >BOOLTYPE</Override> <!-- optional -->
|
||||
</SetCredit>
|
||||
<!-- BEGIN OR -->
|
||||
<InvoiceLineAdd defMacro="MACROTYPE"> <!-- optional -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<Rate >PRICETYPE</Rate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<RatePercent >PERCENTTYPE</RatePercent> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<PriceLevelRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</PriceLevelRef>
|
||||
<!-- END OR -->
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<!-- OptionForPriceRuleConflict may have one of the following values: Zero, BasePrice -->
|
||||
<OptionForPriceRuleConflict >ENUMTYPE</OptionForPriceRuleConflict> <!-- optional -->
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ServiceDate >DATETYPE</ServiceDate> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<OverrideItemAccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideItemAccountRef>
|
||||
<Other1 >STRTYPE</Other1> <!-- optional -->
|
||||
<Other2 >STRTYPE</Other2> <!-- optional -->
|
||||
<LinkToTxn> <!-- optional -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
</LinkToTxn>
|
||||
<DataExt> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- required -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExt>
|
||||
</InvoiceLineAdd>
|
||||
<!-- OR -->
|
||||
<InvoiceLineGroupAdd> <!-- optional -->
|
||||
<ItemGroupRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemGroupRef>
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<DataExt> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- required -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExt>
|
||||
</InvoiceLineGroupAdd>
|
||||
<!-- END OR -->
|
||||
</InvoiceAdd>
|
||||
<IncludeRetElement >STRTYPE</IncludeRetElement> <!-- optional, may repeat -->
|
||||
</InvoiceAddRq>
|
||||
|
||||
<InvoiceAddRs statusCode="INTTYPE" statusSeverity="STRTYPE" statusMessage="STRTYPE">
|
||||
<InvoiceRet> <!-- optional -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<TimeCreated >DATETIMETYPE</TimeCreated> <!-- required -->
|
||||
<TimeModified >DATETIMETYPE</TimeModified> <!-- required -->
|
||||
<EditSequence >STRTYPE</EditSequence> <!-- required -->
|
||||
<TxnNumber >INTTYPE</TxnNumber> <!-- optional -->
|
||||
<CustomerRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerRef>
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<ARAccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ARAccountRef>
|
||||
<TemplateRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</TemplateRef>
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<BillAddress> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
<City >STRTYPE</City> <!-- optional -->
|
||||
<State >STRTYPE</State> <!-- optional -->
|
||||
<PostalCode >STRTYPE</PostalCode> <!-- optional -->
|
||||
<Country >STRTYPE</Country> <!-- optional -->
|
||||
<Note >STRTYPE</Note> <!-- optional -->
|
||||
</BillAddress>
|
||||
<BillAddressBlock> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
</BillAddressBlock>
|
||||
<ShipAddress> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
<City >STRTYPE</City> <!-- optional -->
|
||||
<State >STRTYPE</State> <!-- optional -->
|
||||
<PostalCode >STRTYPE</PostalCode> <!-- optional -->
|
||||
<Country >STRTYPE</Country> <!-- optional -->
|
||||
<Note >STRTYPE</Note> <!-- optional -->
|
||||
</ShipAddress>
|
||||
<ShipAddressBlock> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
</ShipAddressBlock>
|
||||
<IsPending >BOOLTYPE</IsPending> <!-- optional -->
|
||||
<IsFinanceCharge >BOOLTYPE</IsFinanceCharge> <!-- optional -->
|
||||
<PONumber >STRTYPE</PONumber> <!-- optional -->
|
||||
<TermsRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</TermsRef>
|
||||
<DueDate >DATETYPE</DueDate> <!-- optional -->
|
||||
<SalesRepRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesRepRef>
|
||||
<FOB >STRTYPE</FOB> <!-- optional -->
|
||||
<ShipDate >DATETYPE</ShipDate> <!-- optional -->
|
||||
<ShipMethodRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ShipMethodRef>
|
||||
<Subtotal >AMTTYPE</Subtotal> <!-- optional -->
|
||||
<ItemSalesTaxRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemSalesTaxRef>
|
||||
<SalesTaxPercentage >PERCENTTYPE</SalesTaxPercentage> <!-- optional -->
|
||||
<SalesTaxTotal >AMTTYPE</SalesTaxTotal> <!-- optional -->
|
||||
<AppliedAmount >AMTTYPE</AppliedAmount> <!-- optional -->
|
||||
<BalanceRemaining >AMTTYPE</BalanceRemaining> <!-- optional -->
|
||||
<CurrencyRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CurrencyRef>
|
||||
<ExchangeRate >FLOATTYPE</ExchangeRate> <!-- optional -->
|
||||
<BalanceRemainingInHomeCurrency >AMTTYPE</BalanceRemainingInHomeCurrency> <!-- optional -->
|
||||
<Memo >STRTYPE</Memo> <!-- optional -->
|
||||
<IsPaid >BOOLTYPE</IsPaid> <!-- optional -->
|
||||
<CustomerMsgRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerMsgRef>
|
||||
<IsToBePrinted >BOOLTYPE</IsToBePrinted> <!-- optional -->
|
||||
<IsToBeEmailed >BOOLTYPE</IsToBeEmailed> <!-- optional -->
|
||||
<IsTaxIncluded >BOOLTYPE</IsTaxIncluded> <!-- optional -->
|
||||
<CustomerSalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerSalesTaxCodeRef>
|
||||
<SuggestedDiscountAmount >AMTTYPE</SuggestedDiscountAmount> <!-- optional -->
|
||||
<SuggestedDiscountDate >DATETYPE</SuggestedDiscountDate> <!-- optional -->
|
||||
<Other >STRTYPE</Other> <!-- optional -->
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
<LinkedTxn> <!-- optional, may repeat -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<!-- TxnType may have one of the following values: ARRefundCreditCard, Bill, BillPaymentCheck, BillPaymentCreditCard, BuildAssembly, Charge, Check, CreditCardCharge, CreditCardCredit, CreditMemo, Deposit, Estimate, InventoryAdjustment, Invoice, ItemReceipt, JournalEntry, LiabilityAdjustment, Paycheck, PayrollLiabilityCheck, PurchaseOrder, ReceivePayment, SalesOrder, SalesReceipt, SalesTaxPaymentCheck, Transfer, VendorCredit, YTDAdjustment -->
|
||||
<TxnType >ENUMTYPE</TxnType> <!-- required -->
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<!-- LinkType may have one of the following values: AMTTYPE, QUANTYPE -->
|
||||
<LinkType >ENUMTYPE</LinkType> <!-- optional -->
|
||||
<Amount >AMTTYPE</Amount> <!-- required -->
|
||||
</LinkedTxn>
|
||||
<!-- BEGIN OR -->
|
||||
<InvoiceLineRet> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<!-- BEGIN OR -->
|
||||
<Rate >PRICETYPE</Rate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<RatePercent >PERCENTTYPE</RatePercent> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ExpirationDateForSerialLotNumber>STRTYPE</ExpirationDateForSerialLotNumber>
|
||||
<ServiceDate >DATETYPE</ServiceDate> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<Other1 >STRTYPE</Other1> <!-- optional -->
|
||||
<Other2 >STRTYPE</Other2> <!-- optional -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</InvoiceLineRet>
|
||||
<!-- OR -->
|
||||
<InvoiceLineGroupRet> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemGroupRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemGroupRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<IsPrintItemsInGroup >BOOLTYPE</IsPrintItemsInGroup> <!-- required -->
|
||||
<TotalAmount >AMTTYPE</TotalAmount> <!-- required -->
|
||||
<InvoiceLineRet> <!-- optional, may repeat -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<!-- BEGIN OR -->
|
||||
<Rate >PRICETYPE</Rate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<RatePercent >PERCENTTYPE</RatePercent> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ExpirationDateForSerialLotNumber>STRTYPE</ExpirationDateForSerialLotNumber>
|
||||
<ServiceDate >DATETYPE</ServiceDate> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<Other1 >STRTYPE</Other1> <!-- optional -->
|
||||
<Other2 >STRTYPE</Other2> <!-- optional -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</InvoiceLineRet>
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</InvoiceLineGroupRet>
|
||||
<!-- END OR -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</InvoiceRet>
|
||||
<ErrorRecovery> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<TxnNumber >INTTYPE</TxnNumber> <!-- optional -->
|
||||
<EditSequence >STRTYPE</EditSequence> <!-- optional -->
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
</ErrorRecovery>
|
||||
</InvoiceAddRs>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
587
SO_to_Inv/InvoiceQuery.py
Normal file
587
SO_to_Inv/InvoiceQuery.py
Normal file
@ -0,0 +1,587 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
import win32com.client
|
||||
import xmltodict
|
||||
import pprint
|
||||
import datetime
|
||||
import pandas as pd
|
||||
from datetime import date
|
||||
import timeit
|
||||
import os
|
||||
|
||||
class InvoiceQuery:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
# print(f'kwargs:{kwargs}')
|
||||
# print(args)
|
||||
# self.InvoiceList=[]
|
||||
self.PriceLevelName = None
|
||||
self.SPPriceLevelName = None
|
||||
self.item_inventory_path = "ItemInventory"
|
||||
self.price_level_filename = "PriceLevel.xlsx"
|
||||
self._df_price_level = pd.read_excel(os.path.join(os.getcwd(), self.item_inventory_path, self.price_level_filename), usecols=['FullName', 'PriceLevelName', 'CustomPrice'],)
|
||||
print(self._df_price_level)
|
||||
print(type(self._df_price_level.loc[(self._df_price_level['FullName']=="ECO:0:ECO-002") & (self._df_price_level['PriceLevelName']=="T 202202")].values.tolist()[0][2]))
|
||||
self.FullName = kwargs['FullName'] if 'FullName' in kwargs else None
|
||||
|
||||
self.CustomerPriceLevelName_filename = "CustomerList.xlsx"
|
||||
self._df_customer = pd.read_excel(os.path.join(os.getcwd(), self.item_inventory_path, self.CustomerPriceLevelName_filename), usecols=['FullName', 'PriceLevelName', 'SPName'],)
|
||||
self._df_customer = self._df_customer.fillna('')
|
||||
print(self._df_customer)
|
||||
|
||||
self.Customer = self._df_customer.loc[(self._df_customer["FullName"]==self.FullName)].values.tolist()[0]
|
||||
print(f'Customer:{self.Customer}')
|
||||
self.DN = kwargs['DN'] if 'DN' in kwargs else {}
|
||||
self.Reuse = kwargs['Reuse'] if 'Reuse' in kwargs else None
|
||||
|
||||
self.InvoiceList= kwargs['InvoiceList'] if 'InvoiceList' in kwargs else []
|
||||
self.InvoiceList = None
|
||||
self.InvoiceType = kwargs['InvoiceType'] if 'InvoiceType' in kwargs else 'SalesByCustomerSummary'
|
||||
self.IncludeLineItems = kwargs['IncludeLineItems'] if 'IncludeLineItems' in kwargs else 'true'
|
||||
self.IncludeRetElement = kwargs['IncludeRetElement'] if 'IncludeRetElement' in kwargs else []
|
||||
self.TxnDateRangeFilter = kwargs['TxnDateRangeFilter'] if 'TxnDateRangeFilter' in kwargs else None
|
||||
self.DateMacro = None
|
||||
self.MaxReturned = kwargs['MaxReturned'] if 'MaxReturned' in kwargs else '1'
|
||||
|
||||
if 'DateMacro' in kwargs:
|
||||
if kwargs['DateMacro'] in ['All', 'Today', 'ThisWeek', 'ThisWeekToDate', 'ThisMonth', 'ThisMonthToDate', 'ThisQuarter', 'ThisQuarterToDate', 'ThisYear', 'ThisYearToDate', 'Yesterday', 'LastWeek', 'LastWeekToDate', 'LastMonth', 'LastMonthToDate', 'LastQuarter', 'LastQuarterToDate', 'LastYear', 'LastYearToDate', 'NextWeek', 'NextFourWeeks', 'NextMonth', 'NextQuarter', 'NextYear']:
|
||||
self.DateMacro = kwargs['DateMacro']
|
||||
today = datetime.date.today()
|
||||
deltatoday = today - datetime.timedelta(days = 74)
|
||||
print(today, deltatoday)
|
||||
self.FromTxnDate = self.validate_date(kwargs['FromTxnDate']) if 'FromTxnDate' in kwargs else deltatoday
|
||||
self.ToTxnDate = self.validate_date(kwargs['ToTxnDate']) if 'ToTxnDate' in kwargs else today
|
||||
self.ReportEntityFilter = kwargs['ReportEntityFilter'] if 'ReportEntityFilter' in kwargs else None
|
||||
|
||||
# print(self.DateMacro, self.TxnDateRangeFilter, self.FromTxnDate, self.ToTxnDate)
|
||||
# print(self.Reuse)
|
||||
# if not self.Reuse:
|
||||
# self.dfDN, self.ext_doc_no_list = self.get_ext_doc_no_list(self.DN)
|
||||
# self.RefNumber = kwargs['RefNumber'] if 'RefNumber' in kwargs else self.ext_doc_no_list
|
||||
# self.get_sales_order_header()
|
||||
|
||||
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
|
||||
if type(attrib) is not dict:
|
||||
attrib = {}
|
||||
ele = ET.SubElement(parentNode, thisNode)
|
||||
for x in attrib:
|
||||
ele.set(x, attrib[x])
|
||||
ele.text = text
|
||||
tail = "\n"
|
||||
for x in range(whiteSpace):
|
||||
tail = tail + " "
|
||||
ele.tail = tail
|
||||
return ele
|
||||
|
||||
def create_QBXML(self):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
InvoiceQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "InvoiceQueryRq","\n " )
|
||||
# InvoiceType = self.create_sub_element(ET, InvoiceQueryRq, 'InvoiceType', self.InvoiceType)
|
||||
# if self.FullName:
|
||||
# EntityFilter = self.create_sub_element(ET, InvoiceQueryRq, 'EntityFilter', "\n ")
|
||||
# FullName = self.create_sub_element(ET, EntityFilter, "FullName", self.FullName, 6)
|
||||
# if self.MaxReturned:
|
||||
# MaxReturned = self.create_sub_element(ET, InvoiceQueryRq, 'MaxReturned', self.MaxReturned, 6)
|
||||
if self.DateMacro:
|
||||
TxnDateRangeFilter = self.create_sub_element(ET, InvoiceQueryRq, "TxnDateRangeFilter", "\n ",)
|
||||
InvoiceType = self.create_sub_element(ET, TxnDateRangeFilter, "DateMacro", self.DateMacro)
|
||||
# InvoiceType = self.create_sub_element(ET, InvoiceQueryRq, "DateMacro", self.DateMacro)
|
||||
|
||||
elif type(self.FromTxnDate) is datetime.date or type(self.ToTxnDate) is datetime.date:
|
||||
TxnDateRangeFilter = self.create_sub_element(ET, InvoiceQueryRq, "TxnDateRangeFilter", "\n ",)
|
||||
if type(self.FromTxnDate) is datetime.date:
|
||||
FromTxnDate = self.create_sub_element(ET, TxnDateRangeFilter, "FromTxnDate", self.FromTxnDate.strftime('%Y-%m-%d'),4)
|
||||
if type(self.ToTxnDate) is datetime.date:
|
||||
ToTxnDate = self.create_sub_element(ET, TxnDateRangeFilter, "ToTxnDate", self.ToTxnDate.strftime('%Y-%m-%d'))
|
||||
if self.IncludeLineItems:
|
||||
IncludeLineItems = self.create_sub_element(ET, InvoiceQueryRq, "IncludeLineItems", self.IncludeLineItems, 4)
|
||||
if len(self.IncludeRetElement)>0:
|
||||
for x in self.IncludeRetElement:
|
||||
IncludeRetElement = self.create_sub_element(ET, InvoiceQueryRq, "IncludeRetElement", x, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
# print(f'create_QBXML->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
def get_customer_pricelevel(self, response_string):
|
||||
QBXML = ET.fromstring(response_string)
|
||||
PriceLevelName = QBXML.find('.//PriceLevelRef')
|
||||
if PriceLevelName:
|
||||
|
||||
PriceLevelName = PriceLevelName.find("FullName").text
|
||||
print(f'PriceLevelName:{PriceLevelName}')
|
||||
DataExtRets = QBXML.findall('.//DataExtRet')
|
||||
SP_PriceLevelName = None
|
||||
if len(DataExtRets)>0:
|
||||
for DataExtRet in DataExtRets:
|
||||
DataExtName = DataExtRet.find('DataExtName').text
|
||||
DataExtValue = DataExtRet.find('DataExtValue').text
|
||||
if DataExtName.lower() == 'special cust'.lower():
|
||||
SP_PriceLevelName = DataExtValue.upper()
|
||||
break
|
||||
self.PriceLevelName = PriceLevelName
|
||||
self.SPPriceLevelName = SP_PriceLevelName
|
||||
return PriceLevelName, SP_PriceLevelName
|
||||
|
||||
def create_customerquery_QBXML(self ):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
CustomerQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "CustomerQueryRq","\n " )
|
||||
# InvoiceType = self.create_sub_element(ET, CustomerQueryRq, 'InvoiceType', self.InvoiceType)
|
||||
FullName = self.create_sub_element(ET, CustomerQueryRq, "FullName", self.FullName, 6)
|
||||
IncludeRetElement = ['FullName', 'PriceLevelRef', 'DataExtRet']
|
||||
for x in IncludeRetElement:
|
||||
IncludeRetElement = self.create_sub_element(ET, CustomerQueryRq, "IncludeRetElement", x, 4)
|
||||
OwnerID = self.create_sub_element(ET, CustomerQueryRq, "OwnerID", "0", 6)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_customer_QBXML->qbxml_query: {qbxml_query}')
|
||||
response_string=self.connect_to_quickbooks(qbxml_query)
|
||||
|
||||
return self.get_customer_pricelevel(response_string)
|
||||
return response_string
|
||||
|
||||
def create_invoiceadd_QBXML(self):
|
||||
print('create_ionvoiceadd_QBXML')
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
InvoiceAddRq = self.create_sub_element(ET, QBXMLMsgsRq, "InvoiceAddRq", "\n ",2 )
|
||||
InvoiceAdd = self.create_sub_element(ET, InvoiceAddRq, "InvoiceAdd", "\n ",4 )
|
||||
CustomerRef = self.create_sub_element(ET, InvoiceAdd, "CustomerRef", "\n ",8 )
|
||||
FullName = self.create_sub_element(ET, CustomerRef, "FullName", self.InvoiceList[0]['CustomerFullName'], 8 )
|
||||
TemplateRef = self.create_sub_element(ET, InvoiceAdd, "TemplateRef", "\n ", 8 )
|
||||
TemplateFullName = self.create_sub_element(ET, TemplateRef, "FullName", "DBW Invoice (11%)", 8 )
|
||||
today = str(date.today())
|
||||
TxnDate = self.create_sub_element(ET, InvoiceAdd, "TxnDate", today,8) # self.DN['TxnDate'], 8 )
|
||||
|
||||
RefNumber = self.create_sub_element(ET, InvoiceAdd, "RefNumber", today,8 ) # self.DN['DNRefNum'], 8 )
|
||||
ShipDate = self.create_sub_element(ET, InvoiceAdd, "ShipDate", today,8) # self.DN['TxnDate'], 8 )
|
||||
# Memo = self.create_sub_element(ET, InvoiceAdd, "Memo", self.DN['Memo'], 10 )
|
||||
disc_amount = 0
|
||||
for soidx, salesorder in enumerate(self.InvoiceList):
|
||||
SOTxnId = salesorder['TxnID']
|
||||
disc_amount+=int(salesorder['Disc_Amount'])
|
||||
print(f'create_invoiceadd_QBXML->SOTxnId: {SOTxnId}')
|
||||
for itemline in salesorder['InvoiceLineRet']:
|
||||
# if 'DNQuantity' in itemline:
|
||||
InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
|
||||
# Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", str(itemline['DNQuantity'] ), 12 )
|
||||
# Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", str(itemline['BackOrdered'] ), 12 )
|
||||
# UnitOfMeasure = self.create_sub_element(ET, InvoiceLineAdd, "UnitOfMeasure", str(itemline['UOM']), 12 )
|
||||
LinkToTxn = self.create_sub_element(ET, InvoiceLineAdd, "LinkToTxn", "\n ", 10 )
|
||||
TxnID = self.create_sub_element(ET, LinkToTxn, "TxnID", SOTxnId,14 )
|
||||
TxnLineID = self.create_sub_element(ET, LinkToTxn, "TxnLineID", itemline['TxnLineID'], 12 )
|
||||
if soidx == len(self.InvoiceList)-1: #last list then set the'400_Sales_discount'
|
||||
print(f'disc_amount:{format(disc_amount, ".2f")}')
|
||||
if disc_amount != 0:
|
||||
|
||||
# disc_amount=format(disc_amount, ".2f")
|
||||
InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
|
||||
ItemRef = self.create_sub_element(ET, InvoiceLineAdd, "ItemRef", "\n ", 12)
|
||||
ItemFullName = self.create_sub_element(ET, ItemRef, "FullName", "400_Sales Discount", 12)
|
||||
ItemRate = self.create_sub_element(ET, InvoiceLineAdd, "Rate", str(disc_amount), 10)
|
||||
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
# print(f'create_invoiceadd_QBXML->Create_Invoiceadd_QBXML: {qbxml_query}')
|
||||
# print(f"replyfrom qbxml:{self.connect_to_quickbooks(qbxml_query)}")
|
||||
|
||||
return qbxml_query
|
||||
|
||||
|
||||
def connect_to_quickbooks(self, qbxml_query):
|
||||
# Connect to Quickbooks
|
||||
# sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
# sessionManager.OpenConnection('', 'DASA')
|
||||
# enumfodnc= win32com.client.Dispatch('QBXMLRP2.RequestProcessor')
|
||||
# print(enumfodnc)
|
||||
# print(enumfodnc.qbFileOpenDoNotCare)
|
||||
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
sessionManager.OpenConnection('', 'DASA2')
|
||||
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
|
||||
|
||||
ticket = sessionManager.BeginSession("", 2)
|
||||
|
||||
# Send query and receive response
|
||||
response_string = sessionManager.ProcessRequest(ticket, qbxml_query)
|
||||
|
||||
# Disconnect from Quickbooks
|
||||
sessionManager.EndSession(ticket) # Close the company file
|
||||
sessionManager.CloseConnection() # Close the connection
|
||||
# print (f'response_string:{response_string}')
|
||||
return response_string
|
||||
|
||||
def __str__(self, *args) -> str:
|
||||
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
|
||||
# print("__str__")
|
||||
return str(self.get_sales_order_header())
|
||||
# return "hello"
|
||||
|
||||
def get_sales_order_header(self, *args):
|
||||
return self._get_sales_order_header(self.connect_to_quickbooks(self.create_QBXML()))
|
||||
|
||||
def status_ok(self, QBXML): #for InvoiceAddRS
|
||||
|
||||
tree = ET.fromstring(QBXML)
|
||||
|
||||
GSRQRs = tree.find(".//InvoiceQueryRs")
|
||||
# print(f"GSRQRs:{GSRQRs}")
|
||||
status_code = GSRQRs.attrib #.get('statusCode')
|
||||
# print(GSRQRs.attrib)
|
||||
# print(GSRQRs.attrib['statusCode'])
|
||||
status=GSRQRs.attrib.get('statusMessage')
|
||||
|
||||
print(f'status={status}')
|
||||
if 'OK' in status:
|
||||
return True, status_code
|
||||
else:
|
||||
return False, status_code
|
||||
|
||||
|
||||
def _get_sales_order_header(self, response_string):
|
||||
print('_get_sales_order_header')
|
||||
# print(f'responsestring:{response_string}')
|
||||
QBXML = ET.fromstring(response_string)
|
||||
datadict = {}
|
||||
Invoicedict = {}
|
||||
_Invoicelist = []
|
||||
InvoiceRets = QBXML.findall('.//InvoiceRet')
|
||||
# print(InvoiceRets)
|
||||
for InvoiceRet in InvoiceRets:
|
||||
RefNumber = InvoiceRet.find('RefNumber').text
|
||||
# Memo = InvoiceRet.find('Memo').text
|
||||
CustomerFullName = InvoiceRet.find('CustomerRef/FullName').text
|
||||
TxnID = InvoiceRet.find('TxnID').text
|
||||
TotalAmount = InvoiceRet.find('TotalAmount').text
|
||||
IsFullyInvoiced = InvoiceRet.find('IsFullyInvoiced').text
|
||||
IsManuallyClosed = InvoiceRet.find('IsManuallyClosed').text
|
||||
# print(CustomerFullName, TxnID, TotalAmount)
|
||||
Invoicedict = {'RefNumber':RefNumber, 'CustomerFullName':CustomerFullName, 'TxnID':TxnID,
|
||||
'TotalAmount':TotalAmount, 'IsFullyInvoiced':IsFullyInvoiced, 'IsManuallyClosed':IsManuallyClosed, 'InvoiceLineRet':[]}
|
||||
InvoiceLineRet = InvoiceRet.findall('InvoiceLineRet')
|
||||
# print(len(InvoiceLineRet))
|
||||
if len(InvoiceLineRet) > 0:
|
||||
disc_amount=0
|
||||
for InvoiceLineRet in InvoiceLineRet:
|
||||
TxnLineID = InvoiceLineRet.find('TxnLineID').text
|
||||
ItemFullName = InvoiceLineRet.find('ItemRef/FullName').text
|
||||
Quantity = InvoiceLineRet.find('Quantity').text
|
||||
UnitOfMeasure = InvoiceLineRet.find('UnitOfMeasure').text
|
||||
Rate = float(InvoiceLineRet.find('Rate').text)
|
||||
|
||||
Amount = float(InvoiceLineRet.find('Amount').text)
|
||||
# if self.SPPriceLevelName:
|
||||
if self.Customer[2]:
|
||||
# print(Rate, (Rate - self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.SPPriceLevelName)].values.tolist()[0][2]))
|
||||
print(Quantity, Rate, (Rate - self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2]))
|
||||
disc_amount += float(Quantity) * (Rate-self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2])
|
||||
# disc_amount+=float(Quantity)*2000 #testing only
|
||||
Invoiced = InvoiceLineRet.find('Invoiced').text
|
||||
LineIsManuallyClosed = InvoiceLineRet.find('IsManuallyClosed').text
|
||||
# print(TxnLineID, ItemFullName)
|
||||
BackOrdered = float(Quantity) - float(Invoiced)
|
||||
if BackOrdered:
|
||||
InvoiceLinedict = {'TxnLineID':TxnLineID,
|
||||
'ItemFullName':ItemFullName,
|
||||
'Quantity':Quantity,
|
||||
'UOM':UnitOfMeasure,
|
||||
'Rate':Rate,
|
||||
'Amount':Amount,
|
||||
'BackOrdered':BackOrdered,
|
||||
'Invoiced':Invoiced,
|
||||
'LineIsManuallyClosed':LineIsManuallyClosed,
|
||||
}
|
||||
Invoicedict['InvoiceLineRet'].append(InvoiceLinedict)
|
||||
Invoicedict['Disc_Amount']=disc_amount
|
||||
_Invoicelist.append(Invoicedict)
|
||||
# print(_Invoicelist)
|
||||
self.InvoiceList=_Invoicelist
|
||||
# print(f'_get_sales_order_header->Salesorderlist: {self.InvoiceList}')
|
||||
return self.InvoiceList
|
||||
|
||||
def addDiscountToInvoiceList(self, _dict:dict):
|
||||
print("addDiscountToInvoiceList")
|
||||
|
||||
|
||||
def addDNQtyToInvoiceList(self, _dict:dict):
|
||||
_bolfoundrefnum=False
|
||||
_bol_dictisadded=False
|
||||
Error_msg = None
|
||||
for poidx, _po in enumerate(self.InvoiceList):
|
||||
if _po['RefNumber']==_dict['RefNum']:
|
||||
_bolfoundrefnum=True
|
||||
if len(_po['InvoiceLineRet'])>0:
|
||||
for polineidx, _poline in enumerate(_po['InvoiceLineRet']):
|
||||
pass
|
||||
if _poline['ItemFullName']==_dict['FullName']:
|
||||
if _poline['UOM'].upper()==_dict['UOM'].upper():
|
||||
# first do UOM in _dict convert treatment
|
||||
QuantityIn_dict = _dict['Quantity']
|
||||
if _dict['UOM'].upper().startswith('ROLL_'):
|
||||
print(f"addDNQtyToInvoiceList->DNqty:{_dict['Quantity']}, Roll_:{_dict['UOM'].split('_')[1]}")
|
||||
QuantityIn_dict = _dict['Quantity'] * int(_dict['UOM'].split("_")[1])
|
||||
pass
|
||||
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "CUTTER":
|
||||
print("addDNQtyToInvoiceList->cutter")
|
||||
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("EB-") or _dict['Item No'].upper().startswith("TA-")):
|
||||
print("addDNQtyToInvoiceList->LEM")
|
||||
if _dict['Item No'].split("-")[1].endswith("1006"):
|
||||
QuantityIn_dict = QuantityIn_dict * 12
|
||||
elif _dict['Item No'].split("-")[1].endswith("1025"):
|
||||
QuantityIn_dict = QuantityIn_dict * 6
|
||||
elif _dict['Item No'].split("-")[1].endswith("1100"):
|
||||
pass
|
||||
print("1100 lem")
|
||||
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("TL-") or _dict['Item No'].upper().startswith("TFL-")):
|
||||
print("addDNQtyToInvoiceList->Lock")
|
||||
QuantityIn_dict = QuantityIn_dict * 20
|
||||
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "EDG-TRIMMER":
|
||||
QuantityIn_dict = QuantityIn_dict * 12 #coz box of 12
|
||||
|
||||
|
||||
if _poline['BackOrdered']>=QuantityIn_dict:
|
||||
self.InvoiceList[poidx]['InvoiceLineRet'][polineidx]['DNQuantity']=float(QuantityIn_dict)
|
||||
_bol_dictisadded = True
|
||||
else:
|
||||
print(f"{_poline['ItemFullName']} BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}")
|
||||
Error_msg = f"BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}"
|
||||
else:
|
||||
# print(f"UOM different {_poline['UOM']} <> {_dict['UOM']}")
|
||||
Error_msg = f"UOM different {_poline['UOM']} <> {_dict['UOM']}"
|
||||
else:
|
||||
# print("errorpoline <>DN")
|
||||
Error_msg = f"poline[ItemFullName] <> DN[FullName]; {_poline['ItemFullName']} <> {_dict['FullName']}, maybe there are 2 same namefromtaco in QB"
|
||||
else:
|
||||
pass
|
||||
# print(f"this refnum {_dict['RefNum']} have no QB PO Return Line")
|
||||
Error_msg = f"this refnum {_dict['RefNum']} have no QB PO Return Line"
|
||||
|
||||
# print (_bol_dictisadded, Error_msg)
|
||||
return _bol_dictisadded, Error_msg
|
||||
|
||||
|
||||
def prepareInvoice(self, df:pd.DataFrame = None):
|
||||
# print(df)
|
||||
_bolAllDNareOk = True
|
||||
_notindflist=[]
|
||||
_yescounter = 0
|
||||
_nocounter = 0
|
||||
if df is not None:
|
||||
_dflist = df.to_dict('records')
|
||||
# print(_dflist)
|
||||
|
||||
# else:
|
||||
# _dflist = self.dfDN.to_dict('records')
|
||||
# print(self.dfDN)
|
||||
# print(f'_dflist:{_dflist}')
|
||||
for idx, xdf in enumerate(_dflist):
|
||||
_boladdDN, _Errormsg = self.addDNQtyToInvoiceList(xdf)
|
||||
# print(f'prepareInvoice->_Errormsg:{_Errormsg}')
|
||||
if _boladdDN:
|
||||
_dflist[idx]['ADDED']=True
|
||||
elif _Errormsg:
|
||||
_dflist[idx]['ERROR']=_Errormsg
|
||||
for xdf in (_dflist):
|
||||
if 'ADDED' not in xdf:
|
||||
# print (f"prepareInvoice->not added: {xdf['Item No']}")
|
||||
print (f"prepareInvoice->not added: {xdf}")
|
||||
|
||||
_notindflist.append(xdf)
|
||||
_nocounter+=1
|
||||
_bolAllDNareOk = False
|
||||
else:
|
||||
print (f"ADDED: {xdf['Item No']}")
|
||||
|
||||
print(f'{len(_dflist) - _nocounter} of {len(_dflist)} are added')
|
||||
return _bolAllDNareOk, _notindflist
|
||||
|
||||
|
||||
def validate_date(self, date_text):
|
||||
if date_text is None:
|
||||
return None
|
||||
try:
|
||||
return datetime.datetime.strptime(date_text, '%Y-%m-%d').date()
|
||||
except ValueError:
|
||||
return None
|
||||
# raise ValueError("Incorrect data format, should be YYYY-MM-DD")
|
||||
|
||||
def get_ext_doc_no_list(self, dndict=None):
|
||||
if dndict:
|
||||
dnlist = dndict['lines']
|
||||
else:
|
||||
dnlist = self.DN
|
||||
df = pd.DataFrame(dnlist)
|
||||
# print(df)
|
||||
df['RefNum']= df['No.SO/Ext.Doc.No.'].apply(lambda x: "L"+ x.split("L-")[1])
|
||||
# print(df)
|
||||
ext_doc_no=df['RefNum'].unique().tolist()
|
||||
if len(ext_doc_no)>0:
|
||||
return df, ext_doc_no
|
||||
else:
|
||||
return df, None
|
||||
|
||||
|
||||
def get_last_refnumber(self, response_string=None):
|
||||
if not response_string:
|
||||
response_string = self.connect_to_quickbooks(self.create_QBXML())
|
||||
status, status_msg = self.status_ok(response_string)
|
||||
if not status:
|
||||
return None
|
||||
QBXML = ET.fromstring(response_string)
|
||||
_OpenInvoicelist = []
|
||||
InvoiceRets = QBXML.findall('.//RefNumber')
|
||||
print(f'InvoiceRets count:{len(InvoiceRets)}')
|
||||
refnumbers = []
|
||||
for InvoiceRet in InvoiceRets:
|
||||
print(InvoiceRet, InvoiceRet.text)
|
||||
refnumbers.append(InvoiceRet.text)
|
||||
|
||||
# IsFullyInvoiced = InvoiceRet.find('IsFullyInvoiced').text
|
||||
# IsManuallyClosed = InvoiceRet.find('IsManuallyClosed').text
|
||||
# if IsFullyInvoiced=='false' and IsManuallyClosed=='false':
|
||||
# txndate = InvoiceRet.find('TxnDate').text
|
||||
# totalamount = InvoiceRet.find('TotalAmount').text
|
||||
# refnumber = InvoiceRet.find('RefNumber').text
|
||||
|
||||
# _OpenInvoicelist.append([InvoiceRet.find('TxnID').text, txndate, totalamount, refnumber, ])
|
||||
# # _OpenInvoicelist.append(InvoiceRet.find('TxnID').text)
|
||||
# # RefNumber = InvoiceRet.find('RefNumber').text
|
||||
# # Memo = InvoiceRet.find('Memo').text
|
||||
# # CustomerFullName = InvoiceRet.find('CustomerRef/FullName').text
|
||||
# # TxnID = InvoiceRet.find('TxnID').text
|
||||
# # TotalAmount = InvoiceRet.find('TotalAmount').text
|
||||
# # IsFullyReceived = InvoiceRet.find('IsFullyReceived').text
|
||||
# # IsManuallyClosed = InvoiceRet.find('IsManuallyClosed').text
|
||||
print(refnumbers)
|
||||
refnumbers.sort()
|
||||
return refnumbers[-1]
|
||||
|
||||
def get_open_so(self, response_string=None):
|
||||
if not response_string:
|
||||
response_string = self.connect_to_quickbooks(self.create_QBXML())
|
||||
|
||||
QBXML = ET.fromstring(response_string)
|
||||
_OpenInvoicelist = []
|
||||
InvoiceRets = QBXML.findall('.//InvoiceRet')
|
||||
# print(f'InvoiceRets count:{len(InvoiceRets)}')
|
||||
for InvoiceRet in InvoiceRets:
|
||||
IsFullyInvoiced = InvoiceRet.find('IsFullyInvoiced').text
|
||||
IsManuallyClosed = InvoiceRet.find('IsManuallyClosed').text
|
||||
if IsFullyInvoiced=='false' and IsManuallyClosed=='false':
|
||||
txndate = InvoiceRet.find('TxnDate').text
|
||||
totalamount = InvoiceRet.find('TotalAmount').text
|
||||
refnumber = InvoiceRet.find('RefNumber').text
|
||||
|
||||
_OpenInvoicelist.append([InvoiceRet.find('TxnID').text, txndate, totalamount, refnumber, ])
|
||||
# _OpenInvoicelist.append(InvoiceRet.find('TxnID').text)
|
||||
# RefNumber = InvoiceRet.find('RefNumber').text
|
||||
# Memo = InvoiceRet.find('Memo').text
|
||||
# CustomerFullName = InvoiceRet.find('CustomerRef/FullName').text
|
||||
# TxnID = InvoiceRet.find('TxnID').text
|
||||
# TotalAmount = InvoiceRet.find('TotalAmount').text
|
||||
# IsFullyReceived = InvoiceRet.find('IsFullyReceived').text
|
||||
# IsManuallyClosed = InvoiceRet.find('IsManuallyClosed').text
|
||||
# print(_OpenInvoicelist)
|
||||
return _OpenInvoicelist
|
||||
|
||||
def create_open_sales_order_qbxml(self, txnid:list, IncludeLineItems=True):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
InvoiceQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "InvoiceQueryRq","\n " )
|
||||
if len(txnid)>0:
|
||||
for x in txnid:
|
||||
TxnID = self.create_sub_element(ET, InvoiceQueryRq, "TxnID", x, 6 )
|
||||
else:
|
||||
return None
|
||||
if IncludeLineItems:
|
||||
IncludeLineItems = self.create_sub_element(ET, InvoiceQueryRq, "IncludeLineItems", 'true', 4)
|
||||
# if len(self.IncludeRetElement)>0:
|
||||
# for x in self.IncludeRetElement:
|
||||
# IncludeRetElement = self.create_sub_element(ET, InvoiceQueryRq, "IncludeRetElement", x, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_open_sale_order_qbxml->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
|
||||
def get_open_sales_order(self, txnlist:list):
|
||||
print(f'txnid: {txnlist}', type(txnlist))
|
||||
txnid = []
|
||||
for x in txnlist:
|
||||
if isinstance(x, list):
|
||||
txnid.append(x[0])
|
||||
else:
|
||||
txnid.append(x)
|
||||
break
|
||||
# txnid = [x[0] if isinstance(x, list) else x for x in txnid ]
|
||||
print(txnid)
|
||||
if txnid:
|
||||
# print(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
|
||||
return self._get_sales_order_header(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
|
||||
else:
|
||||
print("There is No Open Invoice Order")
|
||||
return None
|
||||
return None
|
||||
|
||||
print('### InvoiceQuery ###')
|
||||
if __name__ == '__main__':
|
||||
starttime = timeit.default_timer()
|
||||
# ini=InvoiceQuery(FullName= '999 HPL', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'IsManuallyClosed', 'IsFullyInvoiced'])
|
||||
|
||||
# ini=InvoiceQuery(FullName= 'YSM Interior', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
|
||||
ini=InvoiceQuery(FullName= 'Abadi Serpong', IncludeRetElement = 'RefNumber', ])
|
||||
# iya = ini.create_customerquery_QBXML() #pakai excel saja lebih cepat
|
||||
# print(iya)
|
||||
print(f'createQBXML:{ini.create_QBXML()}')
|
||||
response_string = ini.connect_to_quickbooks(ini.create_QBXML())
|
||||
print(f'response_string:{response_string}')
|
||||
# response_string = None
|
||||
last_refnumber = ini.get_last_refnumber(response_string)
|
||||
print(f'open sales orders:{last_refnumber};type:{type(last_refnumber)}')
|
||||
# print('23a'+1)
|
||||
# if open_sales_orders:
|
||||
|
||||
# itu = ini.get_open_sales_order(open_sales_orders)
|
||||
# # print(itu)
|
||||
# print(f'get_open_sales_order:{itu}')
|
||||
# if itu:
|
||||
|
||||
# print(ini.create_invoiceadd_QBXML())
|
||||
|
||||
# # ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML())
|
||||
|
||||
print("The time difference is :", timeit.default_timer() - starttime)
|
||||
444
SO_to_Inv/PriceLevelQuery.py
Normal file
444
SO_to_Inv/PriceLevelQuery.py
Normal file
@ -0,0 +1,444 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
import win32com.client
|
||||
import xmltodict
|
||||
import pprint
|
||||
import datetime
|
||||
import pandas as pd
|
||||
from datetime import date
|
||||
import timeit
|
||||
|
||||
class PriceLevelQuery:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
# print(f'kwargs:{kwargs}')
|
||||
# print(args)
|
||||
# self.SalesOrderList=[]
|
||||
self.IncludeLineItems = kwargs['IncludeLineItems'] if 'IncludeLineItems' in kwargs else 'true'
|
||||
self.IncludeRetElement = kwargs['IncludeRetElement'] if 'IncludeRetElement' in kwargs else []
|
||||
self.TxnDateRangeFilter = kwargs['TxnDateRangeFilter'] if 'TxnDateRangeFilter' in kwargs else None
|
||||
self.FullName = kwargs['FullName'] if 'FullName' in kwargs and isinstance(kwargs['FullName'], list) else []
|
||||
self.NameFilter = kwargs['NameFilter'] if 'NameFilter' in kwargs else None
|
||||
self.ItemRef = kwargs['ItemRef'] if 'ItemRef' in kwargs else None
|
||||
|
||||
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
|
||||
if type(attrib) is not dict:
|
||||
attrib = {}
|
||||
ele = ET.SubElement(parentNode, thisNode)
|
||||
for x in attrib:
|
||||
ele.set(x, attrib[x])
|
||||
ele.text = text
|
||||
tail = "\n"
|
||||
for x in range(whiteSpace):
|
||||
tail = tail + " "
|
||||
ele.tail = tail
|
||||
return ele
|
||||
|
||||
def create_QBXML(self):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
PriceLevelQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "PriceLevelQueryRq","\n " )
|
||||
if len(self.FullName)>0:
|
||||
for x in self.FullName:
|
||||
FullName = self.create_sub_element(ET, PriceLevelQueryRq, "FullName", x, 6)
|
||||
if self.NameFilter:
|
||||
NameFilter = self.create_sub_element(ET, PriceLevelQueryRq, "NameFilter","\n ", 6)
|
||||
MatchCriterion = self.create_sub_element(ET, NameFilter, "MatchCriterion", "StartsWith")
|
||||
Name = self.create_sub_element(ET, NameFilter, "Name", self.NameFilter)
|
||||
if self.ItemRef:
|
||||
ItemRef = self.create_sub_element(ET, PriceLevelQueryRq, "ItemRef", "\n ")
|
||||
ItemRefFullName = self.create_sub_element(ET, ItemRef, "FullName", self.ItemRef)
|
||||
|
||||
if len(self.IncludeRetElement)>0:
|
||||
for x in self.IncludeRetElement:
|
||||
IncludeRetElement = self.create_sub_element(ET, PriceLevelQueryRq, "IncludeRetElement", x, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
# print(f'create_QBXML->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
def create_invoiceadd_QBXML(self):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
InvoiceAddRq = self.create_sub_element(ET, QBXMLMsgsRq, "InvoiceAddRq", "\n ",2 )
|
||||
InvoiceAdd = self.create_sub_element(ET, InvoiceAddRq, "InvoiceAdd", "\n ",4 )
|
||||
CustomerRef = self.create_sub_element(ET, InvoiceAdd, "CustomerRef", "\n ",8 )
|
||||
FullName = self.create_sub_element(ET, CustomerRef, "FullName", self.SalesOrderList[1]['CustomerFullName'], 8 )
|
||||
TemplateRef = self.create_sub_element(ET, InvoiceAdd, "TemplateRef", "\n ", 8 )
|
||||
tempFullName = self.create_sub_element(ET, TemplateRef, "FullName", "DBW Invoice (11%)", 8 )
|
||||
today = str(date.today())
|
||||
TxnDate = self.create_sub_element(ET, InvoiceAdd, "TxnDate", today,8) # self.DN['TxnDate'], 8 )
|
||||
|
||||
RefNumber = self.create_sub_element(ET, InvoiceAdd, "RefNumber", today,8 ) # self.DN['DNRefNum'], 8 )
|
||||
ShipDate = self.create_sub_element(ET, InvoiceAdd, "ShipDate", today,8) # self.DN['TxnDate'], 8 )
|
||||
# Memo = self.create_sub_element(ET, InvoiceAdd, "Memo", self.DN['Memo'], 10 )
|
||||
disc_amount = 0
|
||||
for soidx, salesorder in enumerate(self.SalesOrderList):
|
||||
SOTxnId = salesorder['TxnID']
|
||||
disc_amount+=int(salesorder['Disc_Amount'])
|
||||
print(f'create_invoiceadd_QBXML->SOTxnId: {SOTxnId}')
|
||||
for itemline in salesorder['SalesOrderLineRet']:
|
||||
# if 'DNQuantity' in itemline:
|
||||
InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
|
||||
# Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", str(itemline['DNQuantity'] ), 12 )
|
||||
# Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", str(itemline['BackOrdered'] ), 12 )
|
||||
# UnitOfMeasure = self.create_sub_element(ET, InvoiceLineAdd, "UnitOfMeasure", str(itemline['UOM']), 12 )
|
||||
LinkToTxn = self.create_sub_element(ET, InvoiceLineAdd, "LinkToTxn", "\n ", 10 )
|
||||
TxnID = self.create_sub_element(ET, LinkToTxn, "TxnID", SOTxnId,14 )
|
||||
TxnLineID = self.create_sub_element(ET, LinkToTxn, "TxnLineID", itemline['TxnLineID'], 12 )
|
||||
if soidx == len(self.SalesOrderList)-1: #last list then set the'400_Sales_discount'
|
||||
print(f'disc_amount:{format(disc_amount, ".2f")}')
|
||||
# disc_amount=format(disc_amount, ".2f")
|
||||
InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
|
||||
ItemRef = self.create_sub_element(ET, InvoiceLineAdd, "ItemRef", "\n ", 12)
|
||||
ItemFullName = self.create_sub_element(ET, ItemRef, "FullName", "400_Sales Discount", 12)
|
||||
ItemRate = self.create_sub_element(ET, InvoiceLineAdd, "Rate", str(disc_amount), 10)
|
||||
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_invoiceadd_QBXML->Create_Invoiceadd_QBXML: {qbxml_query}')
|
||||
# print(f"replyfrom qbxml:{self.connect_to_quickbooks(qbxml_query)}")
|
||||
|
||||
return qbxml_query
|
||||
|
||||
|
||||
def connect_to_quickbooks(self, qbxml_query):
|
||||
# Connect to Quickbooks
|
||||
# sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
# sessionManager.OpenConnection('', 'DASA')
|
||||
# enumfodnc= win32com.client.Dispatch('QBXMLRP2.RequestProcessor')
|
||||
# print(enumfodnc)
|
||||
# print(enumfodnc.qbFileOpenDoNotCare)
|
||||
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
sessionManager.OpenConnection('', 'DASA2')
|
||||
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
|
||||
|
||||
ticket = sessionManager.BeginSession("", 2)
|
||||
|
||||
# Send query and receive response
|
||||
response_string = sessionManager.ProcessRequest(ticket, qbxml_query)
|
||||
|
||||
# Disconnect from Quickbooks
|
||||
sessionManager.EndSession(ticket) # Close the company file
|
||||
sessionManager.CloseConnection() # Close the connection
|
||||
# print (f'response_string:{response_string}')
|
||||
return response_string
|
||||
|
||||
def __str__(self, *args) -> str:
|
||||
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
|
||||
# print("__str__")
|
||||
return str(self.get_sales_order_header())
|
||||
# return "hello"
|
||||
|
||||
def get_sales_order_header(self, *args):
|
||||
return self. _get_sales_order_header(self.connect_to_quickbooks(self.create_QBXML()))
|
||||
|
||||
def status_ok(self, QBXML): #for InvoiceAddRS
|
||||
|
||||
tree = ET.fromstring(QBXML)
|
||||
|
||||
GSRQRs = tree.find(".//PriceLevelQueryRs")
|
||||
# print(f"GSRQRs:{GSRQRs}")
|
||||
status_code = GSRQRs.attrib #.get('statusCode')
|
||||
# print(GSRQRs.attrib)
|
||||
# print(GSRQRs.attrib['statusCode'])
|
||||
status=GSRQRs.attrib.get('statusMessage')
|
||||
|
||||
print(f'status={status}')
|
||||
if 'OK' in status:
|
||||
return True, status_code
|
||||
else:
|
||||
return False, status_code
|
||||
|
||||
|
||||
def _get_sales_order_header(self, response_string):
|
||||
print('_get_sales_order_header')
|
||||
# print(f'responsestring:{response_string}')
|
||||
QBXML = ET.fromstring(response_string)
|
||||
datadict = {}
|
||||
SalesOrderdict = {}
|
||||
_SalesOrderlist = []
|
||||
SalesOrderRets = QBXML.findall('.//SalesOrderRet')
|
||||
# print(SalesOrderRets)
|
||||
for SalesOrderRet in SalesOrderRets:
|
||||
RefNumber = SalesOrderRet.find('RefNumber').text
|
||||
# Memo = SalesOrderRet.find('Memo').text
|
||||
CustomerFullName = SalesOrderRet.find('CustomerRef/FullName').text
|
||||
TxnID = SalesOrderRet.find('TxnID').text
|
||||
TotalAmount = SalesOrderRet.find('TotalAmount').text
|
||||
IsFullyInvoiced = SalesOrderRet.find('IsFullyInvoiced').text
|
||||
IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
|
||||
# print(CustomerFullName, TxnID, TotalAmount)
|
||||
SalesOrderdict = {'RefNumber':RefNumber, 'CustomerFullName':CustomerFullName, 'TxnID':TxnID,
|
||||
'TotalAmount':TotalAmount, 'IsFullyInvoiced':IsFullyInvoiced, 'IsManuallyClosed':IsManuallyClosed, 'SalesOrderLineRet':[]}
|
||||
SalesOrderLineRet = SalesOrderRet.findall('SalesOrderLineRet')
|
||||
# print(len(SalesOrderLineRet))
|
||||
if len(SalesOrderLineRet) > 0:
|
||||
disc_amount=0
|
||||
for SalesOrderLineRet in SalesOrderLineRet:
|
||||
TxnLineID = SalesOrderLineRet.find('TxnLineID').text
|
||||
ItemFullName = SalesOrderLineRet.find('ItemRef/FullName').text
|
||||
Quantity = SalesOrderLineRet.find('Quantity').text
|
||||
UnitOfMeasure = SalesOrderLineRet.find('UnitOfMeasure').text
|
||||
Rate = float(SalesOrderLineRet.find('Rate').text)
|
||||
|
||||
Amount = float(SalesOrderLineRet.find('Amount').text)
|
||||
disc_amount+=float(Quantity)*2000 #testing only
|
||||
Invoiced = SalesOrderLineRet.find('Invoiced').text
|
||||
LineIsManuallyClosed = SalesOrderLineRet.find('IsManuallyClosed').text
|
||||
# print(TxnLineID, ItemFullName)
|
||||
BackOrdered = float(Quantity) - float(Invoiced)
|
||||
if BackOrdered:
|
||||
SalesOrderLinedict = {'TxnLineID':TxnLineID,
|
||||
'ItemFullName':ItemFullName,
|
||||
'Quantity':Quantity,
|
||||
'UOM':UnitOfMeasure,
|
||||
'Rate':Rate,
|
||||
'Amount':Amount,
|
||||
'BackOrdered':BackOrdered,
|
||||
'Invoiced':Invoiced,
|
||||
'LineIsManuallyClosed':LineIsManuallyClosed,
|
||||
}
|
||||
SalesOrderdict['SalesOrderLineRet'].append(SalesOrderLinedict)
|
||||
SalesOrderdict['Disc_Amount']=disc_amount
|
||||
_SalesOrderlist.append(SalesOrderdict)
|
||||
# print(_SalesOrderlist)
|
||||
self.SalesOrderList=_SalesOrderlist
|
||||
# print(f'_get_sales_order_header->Salesorderlist: {self.SalesOrderList}')
|
||||
return self.SalesOrderList
|
||||
|
||||
def addDiscountToInvoiceList(self, _dict:dict):
|
||||
print("addDiscountToInvoiceList")
|
||||
|
||||
|
||||
def addDNQtyToSalesOrderList(self, _dict:dict):
|
||||
_bolfoundrefnum=False
|
||||
_bol_dictisadded=False
|
||||
Error_msg = None
|
||||
for poidx, _po in enumerate(self.SalesOrderList):
|
||||
if _po['RefNumber']==_dict['RefNum']:
|
||||
_bolfoundrefnum=True
|
||||
if len(_po['SalesOrderLineRet'])>0:
|
||||
for polineidx, _poline in enumerate(_po['SalesOrderLineRet']):
|
||||
pass
|
||||
if _poline['ItemFullName']==_dict['FullName']:
|
||||
if _poline['UOM'].upper()==_dict['UOM'].upper():
|
||||
# first do UOM in _dict convert treatment
|
||||
QuantityIn_dict = _dict['Quantity']
|
||||
if _dict['UOM'].upper().startswith('ROLL_'):
|
||||
print(f"addDNQtyToSalesOrderList->DNqty:{_dict['Quantity']}, Roll_:{_dict['UOM'].split('_')[1]}")
|
||||
QuantityIn_dict = _dict['Quantity'] * int(_dict['UOM'].split("_")[1])
|
||||
pass
|
||||
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "CUTTER":
|
||||
print("addDNQtyToSalesOrderList->cutter")
|
||||
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("EB-") or _dict['Item No'].upper().startswith("TA-")):
|
||||
print("addDNQtyToSalesOrderList->LEM")
|
||||
if _dict['Item No'].split("-")[1].endswith("1006"):
|
||||
QuantityIn_dict = QuantityIn_dict * 12
|
||||
elif _dict['Item No'].split("-")[1].endswith("1025"):
|
||||
QuantityIn_dict = QuantityIn_dict * 6
|
||||
elif _dict['Item No'].split("-")[1].endswith("1100"):
|
||||
pass
|
||||
print("1100 lem")
|
||||
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("TL-") or _dict['Item No'].upper().startswith("TFL-")):
|
||||
print("addDNQtyToSalesOrderList->Lock")
|
||||
QuantityIn_dict = QuantityIn_dict * 20
|
||||
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "EDG-TRIMMER":
|
||||
QuantityIn_dict = QuantityIn_dict * 12 #coz box of 12
|
||||
|
||||
|
||||
if _poline['BackOrdered']>=QuantityIn_dict:
|
||||
self.SalesOrderList[poidx]['SalesOrderLineRet'][polineidx]['DNQuantity']=float(QuantityIn_dict)
|
||||
_bol_dictisadded = True
|
||||
else:
|
||||
print(f"{_poline['ItemFullName']} BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}")
|
||||
Error_msg = f"BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}"
|
||||
else:
|
||||
# print(f"UOM different {_poline['UOM']} <> {_dict['UOM']}")
|
||||
Error_msg = f"UOM different {_poline['UOM']} <> {_dict['UOM']}"
|
||||
else:
|
||||
# print("errorpoline <>DN")
|
||||
Error_msg = f"poline[ItemFullName] <> DN[FullName]; {_poline['ItemFullName']} <> {_dict['FullName']}, maybe there are 2 same namefromtaco in QB"
|
||||
else:
|
||||
pass
|
||||
# print(f"this refnum {_dict['RefNum']} have no QB PO Return Line")
|
||||
Error_msg = f"this refnum {_dict['RefNum']} have no QB PO Return Line"
|
||||
|
||||
# print (_bol_dictisadded, Error_msg)
|
||||
return _bol_dictisadded, Error_msg
|
||||
|
||||
|
||||
def prepareInvoice(self, df:pd.DataFrame = None):
|
||||
# print(df)
|
||||
_bolAllDNareOk = True
|
||||
_notindflist=[]
|
||||
_yescounter = 0
|
||||
_nocounter = 0
|
||||
if df is not None:
|
||||
_dflist = df.to_dict('records')
|
||||
# print(_dflist)
|
||||
|
||||
else:
|
||||
_dflist = self.dfDN.to_dict('records')
|
||||
# print(self.dfDN)
|
||||
# print(f'_dflist:{_dflist}')
|
||||
for idx, xdf in enumerate(_dflist):
|
||||
_boladdDN, _Errormsg = self.addDNQtyToSalesOrderList(xdf)
|
||||
# print(f'prepareInvoice->_Errormsg:{_Errormsg}')
|
||||
if _boladdDN:
|
||||
_dflist[idx]['ADDED']=True
|
||||
elif _Errormsg:
|
||||
_dflist[idx]['ERROR']=_Errormsg
|
||||
for xdf in (_dflist):
|
||||
if 'ADDED' not in xdf:
|
||||
# print (f"prepareInvoice->not added: {xdf['Item No']}")
|
||||
print (f"prepareInvoice->not added: {xdf}")
|
||||
|
||||
_notindflist.append(xdf)
|
||||
_nocounter+=1
|
||||
_bolAllDNareOk = False
|
||||
else:
|
||||
print (f"ADDED: {xdf['Item No']}")
|
||||
|
||||
print(f'{len(_dflist) - _nocounter} of {len(_dflist)} are added')
|
||||
return _bolAllDNareOk, _notindflist
|
||||
|
||||
|
||||
def validate_date(self, date_text):
|
||||
if date_text is None:
|
||||
return None
|
||||
try:
|
||||
return datetime.datetime.strptime(date_text, '%Y-%m-%d').date()
|
||||
except ValueError:
|
||||
return None
|
||||
# raise ValueError("Incorrect data format, should be YYYY-MM-DD")
|
||||
|
||||
def get_ext_doc_no_list(self, dndict=None):
|
||||
if dndict:
|
||||
dnlist = dndict['lines']
|
||||
else:
|
||||
dnlist = self.DN
|
||||
df = pd.DataFrame(dnlist)
|
||||
# print(df)
|
||||
df['RefNum']= df['No.SO/Ext.Doc.No.'].apply(lambda x: "L"+ x.split("L-")[1])
|
||||
# print(df)
|
||||
ext_doc_no=df['RefNum'].unique().tolist()
|
||||
if len(ext_doc_no)>0:
|
||||
return df, ext_doc_no
|
||||
else:
|
||||
return df, None
|
||||
|
||||
def get_pricelevel(self, response_string=None):
|
||||
if not response_string:
|
||||
response_string = self.connect_to_quickbooks(self.create_QBXML())
|
||||
statusok, status = self.status_ok(response_string)
|
||||
if statusok:
|
||||
QBXML = ET.fromstring(response_string)
|
||||
PriceLevellist = {}
|
||||
fullnamelist = []
|
||||
custompricelist =[]
|
||||
PriceLevelRets = QBXML.findall('.//PriceLevelRet')
|
||||
PriceLevelNamelist = []
|
||||
# PriceLevelName = QBXML.find('.//Name').text
|
||||
|
||||
# print(f'PriceLevelPerItemRets count:{len(PriceLevelPerItemRets)}')
|
||||
for PriceLevelRet in PriceLevelRets:
|
||||
PriceLevelPerItemRets = PriceLevelRet.findall('.//PriceLevelPerItemRet')
|
||||
PriceLevelName = PriceLevelRet.find('.//Name').text
|
||||
print(f'pricelevelname:{PriceLevelName}')
|
||||
for PriceLevelPerItemRet in PriceLevelPerItemRets:
|
||||
FullName = PriceLevelPerItemRet.find('.//FullName').text
|
||||
fullnamelist.append(FullName)
|
||||
CustomPrice = PriceLevelPerItemRet.find('CustomPrice').text
|
||||
custompricelist.append(CustomPrice)
|
||||
PriceLevelNamelist.append(PriceLevelName)
|
||||
zip(fullnamelist, custompricelist)
|
||||
PriceLevellist['PriceLevelName'] = PriceLevelNamelist
|
||||
PriceLevellist['FullName']= fullnamelist
|
||||
PriceLevellist['CustomPrice']= custompricelist
|
||||
PriceLeveldf = pd.DataFrame.from_dict(PriceLevellist)
|
||||
PriceLeveldf.sort_values(by=['PriceLevelName', 'FullName'], inplace=True)
|
||||
PriceLeveldf=PriceLeveldf.reset_index(drop=True)
|
||||
print(PriceLeveldf)
|
||||
PriceLeveldf.to_excel('ItemInventory\PriceLevel.xlsx', sheet_name=PriceLevelName, index=False )
|
||||
# print(PriceLevellist)
|
||||
return PriceLevellist
|
||||
else:
|
||||
return None
|
||||
|
||||
def create_open_sales_order_qbxml(self, txnid:list, IncludeLineItems=True):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
SalesOrderQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "SalesOrderQueryRq","\n " )
|
||||
if len(txnid)>0:
|
||||
for x in txnid:
|
||||
TxnID = self.create_sub_element(ET, SalesOrderQueryRq, "TxnID", x, 6 )
|
||||
else:
|
||||
return None
|
||||
if IncludeLineItems:
|
||||
IncludeLineItems = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeLineItems", 'true', 4)
|
||||
# if len(self.IncludeRetElement)>0:
|
||||
# for x in self.IncludeRetElement:
|
||||
# IncludeRetElement = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeRetElement", x, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_QBXML->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
|
||||
def get_open_sales_order(self, txnid:list):
|
||||
print(f'txnid: {txnid}', type(txnid))
|
||||
txnid = [x[0] if isinstance(x, list) else x for x in txnid ]
|
||||
if txnid:
|
||||
# print(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
|
||||
return self._get_sales_order_header(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
|
||||
else:
|
||||
print("There is No Open Sales Order")
|
||||
return None
|
||||
return None
|
||||
|
||||
print('### PriceLevelQuery ###')
|
||||
if __name__ == '__main__':
|
||||
starttime = timeit.default_timer()
|
||||
# ini=PriceLevelQuery(FullName= '999 HPL', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'IsManuallyClosed', 'IsFullyInvoiced'])
|
||||
# ini=PriceLevelQuery(FullName= 'abadi serpong', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
|
||||
ini = PriceLevelQuery(FullName = ['t 202202', 'M 202202'], )
|
||||
# ini = PriceLevelQuery( ItemRef="ECO:0:ECO-002")
|
||||
itu = ini.create_QBXML()
|
||||
print(f'createQBXML->main:{itu}')
|
||||
response_string = ini.connect_to_quickbooks(itu)
|
||||
print(f'response_string:{response_string}')
|
||||
response_string = None
|
||||
pricelevel = ini.get_pricelevel()
|
||||
# print(f'pricelevel:{pricelevel}')
|
||||
|
||||
# print(ini.get_open_sales_order(open_sales_orders))
|
||||
|
||||
# print(ini.create_invoiceadd_QBXML())
|
||||
# ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML())
|
||||
|
||||
print("The time difference is :", timeit.default_timer() - starttime)
|
||||
336
SO_to_Inv/SalesOrderQuery.xml
Normal file
336
SO_to_Inv/SalesOrderQuery.xml
Normal file
@ -0,0 +1,336 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?qbxml version="16.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<SalesOrderQueryRq metaData="ENUMTYPE" iterator="ENUMTYPE" iteratorID="UUIDTYPE">
|
||||
<!-- BEGIN OR -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<RefNumberCaseSensitive >STRTYPE</RefNumberCaseSensitive> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<MaxReturned >INTTYPE</MaxReturned> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ModifiedDateRangeFilter> <!-- optional -->
|
||||
<FromModifiedDate >DATETIMETYPE</FromModifiedDate> <!-- optional -->
|
||||
<ToModifiedDate >DATETIMETYPE</ToModifiedDate> <!-- optional -->
|
||||
</ModifiedDateRangeFilter>
|
||||
<!-- OR -->
|
||||
<TxnDateRangeFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<FromTxnDate >DATETYPE</FromTxnDate> <!-- optional -->
|
||||
<ToTxnDate >DATETYPE</ToTxnDate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<!-- DateMacro may have one of the following values: All, Today, ThisWeek, ThisWeekToDate, ThisMonth, ThisMonthToDate, ThisCalendarQuarter, ThisCalendarQuarterToDate, ThisFiscalQuarter, ThisFiscalQuarterToDate, ThisCalendarYear, ThisCalendarYearToDate, ThisFiscalYear, ThisFiscalYearToDate, Yesterday, LastWeek, LastWeekToDate, LastMonth, LastMonthToDate, LastCalendarQuarter, LastCalendarQuarterToDate, LastFiscalQuarter, LastFiscalQuarterToDate, LastCalendarYear, LastCalendarYearToDate, LastFiscalYear, LastFiscalYearToDate, NextWeek, NextFourWeeks, NextMonth, NextCalendarQuarter, NextCalendarYear, NextFiscalQuarter, NextFiscalYear -->
|
||||
<DateMacro >ENUMTYPE</DateMacro> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</TxnDateRangeFilter>
|
||||
<!-- END OR -->
|
||||
<EntityFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</EntityFilter>
|
||||
<!-- BEGIN OR -->
|
||||
<RefNumberFilter> <!-- optional -->
|
||||
<!-- MatchCriterion may have one of the following values: StartsWith, Contains, EndsWith -->
|
||||
<MatchCriterion >ENUMTYPE</MatchCriterion> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- required -->
|
||||
</RefNumberFilter>
|
||||
<!-- OR -->
|
||||
<RefNumberRangeFilter> <!-- optional -->
|
||||
<FromRefNumber >STRTYPE</FromRefNumber> <!-- optional -->
|
||||
<ToRefNumber >STRTYPE</ToRefNumber> <!-- optional -->
|
||||
</RefNumberRangeFilter>
|
||||
<!-- END OR -->
|
||||
<CurrencyFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- END OR -->
|
||||
</CurrencyFilter>
|
||||
<!-- END OR -->
|
||||
<IncludeLineItems >BOOLTYPE</IncludeLineItems> <!-- optional -->
|
||||
<IncludeLinkedTxns >BOOLTYPE</IncludeLinkedTxns> <!-- optional -->
|
||||
<IncludeRetElement >STRTYPE</IncludeRetElement> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional, may repeat -->
|
||||
</SalesOrderQueryRq>
|
||||
|
||||
<SalesOrderQueryRs statusCode="INTTYPE" statusSeverity="STRTYPE" statusMessage="STRTYPE" retCount="INTTYPE" iteratorRemainingCount="INTTYPE" iteratorID="UUIDTYPE">
|
||||
<SalesOrderRet> <!-- optional, may repeat -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<TimeCreated >DATETIMETYPE</TimeCreated> <!-- required -->
|
||||
<TimeModified >DATETIMETYPE</TimeModified> <!-- required -->
|
||||
<EditSequence >STRTYPE</EditSequence> <!-- required -->
|
||||
<TxnNumber >INTTYPE</TxnNumber> <!-- optional -->
|
||||
<CustomerRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerRef>
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<TemplateRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</TemplateRef>
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<BillAddress> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
<City >STRTYPE</City> <!-- optional -->
|
||||
<State >STRTYPE</State> <!-- optional -->
|
||||
<PostalCode >STRTYPE</PostalCode> <!-- optional -->
|
||||
<Country >STRTYPE</Country> <!-- optional -->
|
||||
<Note >STRTYPE</Note> <!-- optional -->
|
||||
</BillAddress>
|
||||
<BillAddressBlock> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
</BillAddressBlock>
|
||||
<ShipAddress> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
<City >STRTYPE</City> <!-- optional -->
|
||||
<State >STRTYPE</State> <!-- optional -->
|
||||
<PostalCode >STRTYPE</PostalCode> <!-- optional -->
|
||||
<Country >STRTYPE</Country> <!-- optional -->
|
||||
<Note >STRTYPE</Note> <!-- optional -->
|
||||
</ShipAddress>
|
||||
<ShipAddressBlock> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
</ShipAddressBlock>
|
||||
<PONumber >STRTYPE</PONumber> <!-- optional -->
|
||||
<TermsRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</TermsRef>
|
||||
<DueDate >DATETYPE</DueDate> <!-- optional -->
|
||||
<SalesRepRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesRepRef>
|
||||
<FOB >STRTYPE</FOB> <!-- optional -->
|
||||
<ShipDate >DATETYPE</ShipDate> <!-- optional -->
|
||||
<ShipMethodRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ShipMethodRef>
|
||||
<Subtotal >AMTTYPE</Subtotal> <!-- optional -->
|
||||
<ItemSalesTaxRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemSalesTaxRef>
|
||||
<SalesTaxPercentage >PERCENTTYPE</SalesTaxPercentage> <!-- optional -->
|
||||
<SalesTaxTotal >AMTTYPE</SalesTaxTotal> <!-- optional -->
|
||||
<TotalAmount >AMTTYPE</TotalAmount> <!-- optional -->
|
||||
<CurrencyRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CurrencyRef>
|
||||
<ExchangeRate >FLOATTYPE</ExchangeRate> <!-- optional -->
|
||||
<TotalAmountInHomeCurrency >AMTTYPE</TotalAmountInHomeCurrency> <!-- optional -->
|
||||
<IsManuallyClosed >BOOLTYPE</IsManuallyClosed> <!-- optional -->
|
||||
<IsFullyInvoiced >BOOLTYPE</IsFullyInvoiced> <!-- optional -->
|
||||
<Memo >STRTYPE</Memo> <!-- optional -->
|
||||
<CustomerMsgRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerMsgRef>
|
||||
<IsToBePrinted >BOOLTYPE</IsToBePrinted> <!-- optional -->
|
||||
<IsToBeEmailed >BOOLTYPE</IsToBeEmailed> <!-- optional -->
|
||||
<IsTaxIncluded >BOOLTYPE</IsTaxIncluded> <!-- optional -->
|
||||
<CustomerSalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerSalesTaxCodeRef>
|
||||
<Other >STRTYPE</Other> <!-- optional -->
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
<LinkedTxn> <!-- optional, may repeat -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<!-- TxnType may have one of the following values: ARRefundCreditCard, Bill, BillPaymentCheck, BillPaymentCreditCard, BuildAssembly, Charge, Check, CreditCardCharge, CreditCardCredit, CreditMemo, Deposit, Estimate, InventoryAdjustment, Invoice, ItemReceipt, JournalEntry, LiabilityAdjustment, Paycheck, PayrollLiabilityCheck, PurchaseOrder, ReceivePayment, SalesOrder, SalesReceipt, SalesTaxPaymentCheck, Transfer, VendorCredit, YTDAdjustment -->
|
||||
<TxnType >ENUMTYPE</TxnType> <!-- required -->
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<!-- LinkType may have one of the following values: AMTTYPE, QUANTYPE -->
|
||||
<LinkType >ENUMTYPE</LinkType> <!-- optional -->
|
||||
<Amount >AMTTYPE</Amount> <!-- required -->
|
||||
</LinkedTxn>
|
||||
<!-- BEGIN OR -->
|
||||
<SalesOrderLineRet> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<!-- BEGIN OR -->
|
||||
<Rate >PRICETYPE</Rate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<RatePercent >PERCENTTYPE</RatePercent> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ExpirationDateForSerialLotNumber>STRTYPE</ExpirationDateForSerialLotNumber> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<Invoiced >QUANTYPE</Invoiced> <!-- optional -->
|
||||
<IsManuallyClosed >BOOLTYPE</IsManuallyClosed> <!-- optional -->
|
||||
<Other1 >STRTYPE</Other1> <!-- optional -->
|
||||
<Other2 >STRTYPE</Other2> <!-- optional -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</SalesOrderLineRet>
|
||||
<!-- OR -->
|
||||
<SalesOrderLineGroupRet> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemGroupRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemGroupRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<IsPrintItemsInGroup >BOOLTYPE</IsPrintItemsInGroup> <!-- required -->
|
||||
<TotalAmount >AMTTYPE</TotalAmount> <!-- required -->
|
||||
<SalesOrderLineRet> <!-- optional, may repeat -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<!-- BEGIN OR -->
|
||||
<Rate >PRICETYPE</Rate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<RatePercent >PERCENTTYPE</RatePercent> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ExpirationDateForSerialLotNumber>STRTYPE</ExpirationDateForSerialLotNumber> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<Invoiced >QUANTYPE</Invoiced> <!-- optional -->
|
||||
<IsManuallyClosed >BOOLTYPE</IsManuallyClosed> <!-- optional -->
|
||||
<Other1 >STRTYPE</Other1> <!-- optional -->
|
||||
<Other2 >STRTYPE</Other2> <!-- optional -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</SalesOrderLineRet>
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</SalesOrderLineGroupRet>
|
||||
<!-- END OR -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
<FulfillmentStatus>ENUMTYPE</FulfillmentStatus> <!-- optional -->
|
||||
<ShippingDetails> <!-- optional -->
|
||||
<ShippingDetailsLineRet> <!-- optional -->
|
||||
<TrackingID>IDTYPE</TrackingID> <!-- optional -->
|
||||
<CarrierName>STRTYPE</CarrierName> <!-- optional -->
|
||||
<ShippingMethod>STRTYPE</ShippingMethod> <!-- optional -->
|
||||
<ShippingCharges>AMTTYPE</ShippingCharges> <!-- optional -->
|
||||
</ShippingDetailsLineRet>
|
||||
</ShippingDetails>
|
||||
<SOChannel>ENUMTYPE</SOChannel> <!-- optional -->
|
||||
<StoreName>STRTYPE</StoreName> <!-- optional -->
|
||||
<StoreType>STRTYPE</StoreType> <!-- optional -->
|
||||
</SalesOrderRet>
|
||||
</SalesOrderQueryRs>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
0
SO_to_Inv/__init__.py
Normal file
0
SO_to_Inv/__init__.py
Normal file
417
SO_to_Inv/invoiceadd.py
Normal file
417
SO_to_Inv/invoiceadd.py
Normal file
@ -0,0 +1,417 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
import win32com.client
|
||||
import xmltodict
|
||||
import pprint
|
||||
import datetime
|
||||
import pandas as pd
|
||||
from datetime import date
|
||||
import timeit
|
||||
|
||||
class SalesOrderQuery:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
# print(f'kwargs:{kwargs}')
|
||||
# print(args)
|
||||
# self.SalesOrderList=[]
|
||||
self.DN = kwargs['DN'] if 'DN' in kwargs else {}
|
||||
self.Reuse = kwargs['Reuse'] if 'Reuse' in kwargs else None
|
||||
|
||||
self.SalesOrderList= kwargs['SalesOrderList'] if 'SalesOrderList' in kwargs else []
|
||||
|
||||
self.SalesOrderType = kwargs['SalesOrderType'] if 'SalesOrderType' in kwargs else 'SalesByCustomerSummary'
|
||||
self.IncludeLineItems = kwargs['IncludeLineItems'] if 'IncludeLineItems' in kwargs else 'true'
|
||||
self.IncludeRetElement = kwargs['IncludeRetElement'] if 'IncludeRetElement' in kwargs else []
|
||||
self.TxnDateRangeFilter = kwargs['TxnDateRangeFilter'] if 'TxnDateRangeFilter' in kwargs else None
|
||||
self.DateMacro = None
|
||||
if 'DateMacro' in kwargs:
|
||||
if kwargs['DateMacro'] in ['All', 'Today', 'ThisWeek', 'ThisWeekToDate', 'ThisMonth', 'ThisMonthToDate', 'ThisQuarter', 'ThisQuarterToDate', 'ThisYear', 'ThisYearToDate', 'Yesterday', 'LastWeek', 'LastWeekToDate', 'LastMonth', 'LastMonthToDate', 'LastQuarter', 'LastQuarterToDate', 'LastYear', 'LastYearToDate', 'NextWeek', 'NextFourWeeks', 'NextMonth', 'NextQuarter', 'NextYear']:
|
||||
self.DateMacro = kwargs['DateMacro']
|
||||
self.FromTxnDate = self.validate_date(kwargs['FromTxnDate']) if 'FromTxnDate' in kwargs else None
|
||||
self.ToTxnDate = self.validate_date(kwargs['ToTxnDate']) if 'ToTxnDate' in kwargs else None
|
||||
self.ReportEntityFilter = kwargs['ReportEntityFilter'] if 'ReportEntityFilter' in kwargs else None
|
||||
self.FullName = kwargs['FullName'] if 'FullName' in kwargs else None
|
||||
# print(self.DateMacro, self.TxnDateRangeFilter, self.FromTxnDate, self.ToTxnDate)
|
||||
# print(self.Reuse)
|
||||
# if not self.Reuse:
|
||||
# self.dfDN, self.ext_doc_no_list = self.get_ext_doc_no_list(self.DN)
|
||||
# self.RefNumber = kwargs['RefNumber'] if 'RefNumber' in kwargs else self.ext_doc_no_list
|
||||
# self.get_sales_order_header()
|
||||
|
||||
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
|
||||
if type(attrib) is not dict:
|
||||
attrib = {}
|
||||
ele = ET.SubElement(parentNode, thisNode)
|
||||
for x in attrib:
|
||||
ele.set(x, attrib[x])
|
||||
ele.text = text
|
||||
tail = "\n"
|
||||
for x in range(whiteSpace):
|
||||
tail = tail + " "
|
||||
ele.tail = tail
|
||||
return ele
|
||||
|
||||
def create_QBXML(self):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
SalesOrderQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "SalesOrderQueryRq","\n " )
|
||||
# SalesOrderType = self.create_sub_element(ET, SalesOrderQueryRq, 'SalesOrderType', self.SalesOrderType)
|
||||
if self.FullName:
|
||||
EntityFilter = self.create_sub_element(ET, SalesOrderQueryRq, 'EntityFilter', "\n ")
|
||||
FullName = self.create_sub_element(ET, EntityFilter, "FullName", self.FullName, 6)
|
||||
# print(f'refnumber:{self.RefNumber}')
|
||||
# if self.RefNumber:
|
||||
# for rn in self.RefNumber:
|
||||
# RefNumber = self.create_sub_element(ET, SalesOrderQueryRq, "RefNumber", f'{rn}', 4)
|
||||
if self.DateMacro:
|
||||
TxnDateRangeFilter = self.create_sub_element(ET, SalesOrderQueryRq, "TxnDateRangeFilter", "\n ",)
|
||||
SalesOrderType = self.create_sub_element(ET, TxnDateRangeFilter, "DateMacro", self.DateMacro)
|
||||
# SalesOrderType = self.create_sub_element(ET, SalesOrderQueryRq, "DateMacro", self.DateMacro)
|
||||
|
||||
elif type(self.FromTxnDate) is datetime.date or type(self.ToTxnDate) is datetime.date:
|
||||
TxnDateRangeFilter = self.create_sub_element(ET, SalesOrderQueryRq, "TxnDateRangeFilter", "\n ",)
|
||||
if type(self.FromTxnDate) is datetime.date:
|
||||
FromTxnDate = self.create_sub_element(ET, TxnDateRangeFilter, "FromTxnDate", self.FromTxnDate.strftime('%Y-%m-%d'),4)
|
||||
if type(self.ToTxnDate) is datetime.date:
|
||||
ToTxnDate = self.create_sub_element(ET, TxnDateRangeFilter, "ToTxnDate", self.ToTxnDate.strftime('%Y-%m-%d'))
|
||||
if self.IncludeLineItems:
|
||||
IncludeLineItems = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeLineItems", self.IncludeLineItems, 4)
|
||||
if len(self.IncludeRetElement)>0:
|
||||
for x in self.IncludeRetElement:
|
||||
IncludeRetElement = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeRetElement", x, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_QBXML->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
def create_invoiceadd_QBXML(self):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
InvoiceAddRq = self.create_sub_element(ET, QBXMLMsgsRq, "InvoiceAddRq", "\n ",2 )
|
||||
InvoiceAdd = self.create_sub_element(ET, InvoiceAddRq, "InvoiceAdd", "\n ",4 )
|
||||
CustomerRef = self.create_sub_element(ET, InvoiceAdd, "CustomerRef", "\n ",8 )
|
||||
FullName = self.create_sub_element(ET, CustomerRef, "FullName", "TACO", 8 )
|
||||
today = str(date.today())
|
||||
TxnDate = self.create_sub_element(ET, InvoiceAdd, "TxnDate", self.DN['TxnDate'], 8 )
|
||||
RefNumber = self.create_sub_element(ET, InvoiceAdd, "RefNumber", self.DN['DNRefNum'], 8 )
|
||||
Memo = self.create_sub_element(ET, InvoiceAdd, "Memo", self.DN['Memo'], 10 )
|
||||
for salesorder in self.SalesOrderList:
|
||||
POTxnId = salesorder['TxnID']
|
||||
print(f'create_invoiceadd_QBXML->POTxnID: {POTxnId}')
|
||||
for itemline in salesorder['SalesOrderLineRet']:
|
||||
if 'DNQuantity' in itemline:
|
||||
ItemLineAdd = self.create_sub_element(ET, InvoiceAdd, "ItemLineAdd", "\n ", 10 )
|
||||
Quantity = self.create_sub_element(ET, ItemLineAdd, "Quantity", str(itemline['DNQuantity'] ), 12 )
|
||||
UnitOfMeasure = self.create_sub_element(ET, ItemLineAdd, "UnitOfMeasure", str(itemline['UOM']), 12 )
|
||||
LinkToTxn = self.create_sub_element(ET, ItemLineAdd, "LinkToTxn", "\n ",10 )
|
||||
TxnID = self.create_sub_element(ET, LinkToTxn, "TxnID", POTxnId,14 )
|
||||
TxnLineID = self.create_sub_element(ET, LinkToTxn, "TxnLineID", itemline['TxnLineID'],12 )
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
# print(f'create_invoiceadd_QBXML->Create_Invoiceadd_QBXML: {qbxml_query}')
|
||||
# print(f"replyfrom qbxml:{self.connect_to_quickbooks(qbxml_query)}")
|
||||
|
||||
return qbxml_query
|
||||
|
||||
|
||||
def connect_to_quickbooks(self, qbxml_query):
|
||||
# Connect to Quickbooks
|
||||
# sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
# sessionManager.OpenConnection('', 'DASA')
|
||||
# enumfodnc= win32com.client.Dispatch('QBXMLRP2.RequestProcessor')
|
||||
# print(enumfodnc)
|
||||
# print(enumfodnc.qbFileOpenDoNotCare)
|
||||
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
sessionManager.OpenConnection('', 'DASA2')
|
||||
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
|
||||
|
||||
ticket = sessionManager.BeginSession("", 2)
|
||||
|
||||
# Send query and receive response
|
||||
response_string = sessionManager.ProcessRequest(ticket, qbxml_query)
|
||||
|
||||
# Disconnect from Quickbooks
|
||||
sessionManager.EndSession(ticket) # Close the company file
|
||||
sessionManager.CloseConnection() # Close the connection
|
||||
print (response_string)
|
||||
return response_string
|
||||
|
||||
def __str__(self, *args) -> str:
|
||||
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
|
||||
# print("__str__")
|
||||
return str(self.get_sales_order_header())
|
||||
# return "hello"
|
||||
|
||||
def get_sales_order_header(self, *args):
|
||||
return self. _get_sales_order_header(self.connect_to_quickbooks(self.create_QBXML()))
|
||||
|
||||
def status_ok(self, QBXML): #for InvoiceAddRS
|
||||
|
||||
tree = ET.fromstring(QBXML)
|
||||
|
||||
GSRQRs = tree.find(".//InvoiceAddRs")
|
||||
# print(f"GSRQRs:{GSRQRs}")
|
||||
status_code = GSRQRs.attrib #.get('statusCode')
|
||||
# print(GSRQRs.attrib)
|
||||
# print(GSRQRs.attrib['statusCode'])
|
||||
status=GSRQRs.attrib.get('statusMessage')
|
||||
|
||||
print(f'status={status}')
|
||||
if 'OK' in status:
|
||||
return True, status_code
|
||||
else:
|
||||
return False, status_code
|
||||
|
||||
|
||||
def _get_sales_order_header(self, response_string):
|
||||
print('_get_sales_order_header')
|
||||
# print(f'responsestring:{response_string}')
|
||||
QBXML = ET.fromstring(response_string)
|
||||
datadict = {}
|
||||
SalesOrderdict = {}
|
||||
_SalesOrderlist = []
|
||||
SalesOrderRets = QBXML.findall('.//SalesOrderRet')
|
||||
print(SalesOrderRets)
|
||||
for SalesOrderRet in SalesOrderRets:
|
||||
RefNumber = SalesOrderRet.find('RefNumber').text
|
||||
# Memo = SalesOrderRet.find('Memo').text
|
||||
CustomerFullName = SalesOrderRet.find('CustomerRef/FullName').text
|
||||
TxnID = SalesOrderRet.find('TxnID').text
|
||||
TotalAmount = SalesOrderRet.find('TotalAmount').text
|
||||
IsFullyInvoiced = SalesOrderRet.find('IsFullyInvoiced').text
|
||||
IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
|
||||
# print(CustomerFullName, TxnID, TotalAmount)
|
||||
SalesOrderdict = {'RefNumber':RefNumber, 'CustomerFullName':CustomerFullName, 'TxnID':TxnID,
|
||||
'TotalAmount':TotalAmount, 'IsFullyInvoiced':IsFullyInvoiced, 'IsManuallyClosed':IsManuallyClosed, 'SalesOrderLineRet':[]}
|
||||
SalesOrderLineRet = SalesOrderRet.findall('SalesOrderLineRet')
|
||||
# print(len(SalesOrderLineRet))
|
||||
if len(SalesOrderLineRet) > 0:
|
||||
for SalesOrderLineRet in SalesOrderLineRet:
|
||||
pass
|
||||
TxnLineID = SalesOrderLineRet.find('TxnLineID').text
|
||||
ItemFullName = SalesOrderLineRet.find('ItemRef/FullName').text
|
||||
Quantity = SalesOrderLineRet.find('Quantity').text
|
||||
UnitOfMeasure = SalesOrderLineRet.find('UnitOfMeasure').text
|
||||
Rate = float(SalesOrderLineRet.find('Rate').text)
|
||||
Amount = float(SalesOrderLineRet.find('Amount').text)
|
||||
Invoiced = SalesOrderLineRet.find('Invoiced').text
|
||||
# IsBilled = SalesOrderLineRet.find('IsBilled').text
|
||||
LineIsManuallyClosed = SalesOrderLineRet.find('IsManuallyClosed').text
|
||||
# print(TxnLineID, ItemFullName)
|
||||
BackOrdered = float(Quantity) - float(Invoiced)
|
||||
if BackOrdered:
|
||||
SalesOrderLinedict = {'TxnLineID':TxnLineID,
|
||||
'ItemFullName':ItemFullName,
|
||||
'Quantity':Quantity,
|
||||
'UOM':UnitOfMeasure,
|
||||
'Rate':Rate,
|
||||
'Amount':Amount,
|
||||
'BackOrdered':BackOrdered,
|
||||
'Invoiced':Invoiced,
|
||||
# 'IsBilled':IsBilled,
|
||||
'LineIsManuallyClosed':LineIsManuallyClosed,
|
||||
}
|
||||
SalesOrderdict['SalesOrderLineRet'].append(SalesOrderLinedict)
|
||||
_SalesOrderlist.append(SalesOrderdict)
|
||||
# print(_SalesOrderlist)
|
||||
self.SalesOrderList=_SalesOrderlist
|
||||
print(f'_get_sales_order_header->Salesorderlist: {self.SalesOrderList}')
|
||||
# return SalesOrderlist
|
||||
|
||||
def addDNQtyToSalesOrderList(self, _dict:dict):
|
||||
_bolfoundrefnum=False
|
||||
_bol_dictisadded=False
|
||||
Error_msg = None
|
||||
for poidx, _po in enumerate(self.SalesOrderList):
|
||||
if _po['RefNumber']==_dict['RefNum']:
|
||||
_bolfoundrefnum=True
|
||||
if len(_po['SalesOrderLineRet'])>0:
|
||||
for polineidx, _poline in enumerate(_po['SalesOrderLineRet']):
|
||||
pass
|
||||
if _poline['ItemFullName']==_dict['FullName']:
|
||||
if _poline['UOM'].upper()==_dict['UOM'].upper():
|
||||
# first do UOM in _dict convert treatment
|
||||
QuantityIn_dict = _dict['Quantity']
|
||||
if _dict['UOM'].upper().startswith('ROLL_'):
|
||||
print(f"addDNQtyToSalesOrderList->DNqty:{_dict['Quantity']}, Roll_:{_dict['UOM'].split('_')[1]}")
|
||||
QuantityIn_dict = _dict['Quantity'] * int(_dict['UOM'].split("_")[1])
|
||||
pass
|
||||
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "CUTTER":
|
||||
print("addDNQtyToSalesOrderList->cutter")
|
||||
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("EB-") or _dict['Item No'].upper().startswith("TA-")):
|
||||
print("addDNQtyToSalesOrderList->LEM")
|
||||
if _dict['Item No'].split("-")[1].endswith("1006"):
|
||||
QuantityIn_dict = QuantityIn_dict * 12
|
||||
elif _dict['Item No'].split("-")[1].endswith("1025"):
|
||||
QuantityIn_dict = QuantityIn_dict * 6
|
||||
elif _dict['Item No'].split("-")[1].endswith("1100"):
|
||||
pass
|
||||
print("1100 lem")
|
||||
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("TL-") or _dict['Item No'].upper().startswith("TFL-")):
|
||||
print("addDNQtyToSalesOrderList->Lock")
|
||||
QuantityIn_dict = QuantityIn_dict * 20
|
||||
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "EDG-TRIMMER":
|
||||
QuantityIn_dict = QuantityIn_dict * 12 #coz box of 12
|
||||
|
||||
|
||||
if _poline['BackOrdered']>=QuantityIn_dict:
|
||||
self.SalesOrderList[poidx]['SalesOrderLineRet'][polineidx]['DNQuantity']=float(QuantityIn_dict)
|
||||
_bol_dictisadded = True
|
||||
else:
|
||||
print(f"{_poline['ItemFullName']} BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}")
|
||||
Error_msg = f"BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}"
|
||||
else:
|
||||
# print(f"UOM different {_poline['UOM']} <> {_dict['UOM']}")
|
||||
Error_msg = f"UOM different {_poline['UOM']} <> {_dict['UOM']}"
|
||||
else:
|
||||
# print("errorpoline <>DN")
|
||||
Error_msg = f"poline[ItemFullName] <> DN[FullName]; {_poline['ItemFullName']} <> {_dict['FullName']}, maybe there are 2 same namefromtaco in QB"
|
||||
else:
|
||||
pass
|
||||
# print(f"this refnum {_dict['RefNum']} have no QB PO Return Line")
|
||||
Error_msg = f"this refnum {_dict['RefNum']} have no QB PO Return Line"
|
||||
|
||||
# print (_bol_dictisadded, Error_msg)
|
||||
return _bol_dictisadded, Error_msg
|
||||
|
||||
|
||||
def prepareInvoice(self, df:pd.DataFrame = None):
|
||||
# print(df)
|
||||
_bolAllDNareOk = True
|
||||
_notindflist=[]
|
||||
_yescounter = 0
|
||||
_nocounter = 0
|
||||
if df is not None:
|
||||
_dflist = df.to_dict('records')
|
||||
# print(_dflist)
|
||||
|
||||
else:
|
||||
_dflist = self.dfDN.to_dict('records')
|
||||
# print(self.dfDN)
|
||||
# print(f'_dflist:{_dflist}')
|
||||
for idx, xdf in enumerate(_dflist):
|
||||
_boladdDN, _Errormsg = self.addDNQtyToSalesOrderList(xdf)
|
||||
# print(f'prepareInvoice->_Errormsg:{_Errormsg}')
|
||||
if _boladdDN:
|
||||
_dflist[idx]['ADDED']=True
|
||||
elif _Errormsg:
|
||||
_dflist[idx]['ERROR']=_Errormsg
|
||||
for xdf in (_dflist):
|
||||
if 'ADDED' not in xdf:
|
||||
# print (f"prepareInvoice->not added: {xdf['Item No']}")
|
||||
print (f"prepareInvoice->not added: {xdf}")
|
||||
|
||||
_notindflist.append(xdf)
|
||||
_nocounter+=1
|
||||
_bolAllDNareOk = False
|
||||
else:
|
||||
print (f"ADDED: {xdf['Item No']}")
|
||||
|
||||
print(f'{len(_dflist) - _nocounter} of {len(_dflist)} are added')
|
||||
return _bolAllDNareOk, _notindflist
|
||||
|
||||
|
||||
def validate_date(self, date_text):
|
||||
if date_text is None:
|
||||
return None
|
||||
try:
|
||||
return datetime.datetime.strptime(date_text, '%Y-%m-%d').date()
|
||||
except ValueError:
|
||||
return None
|
||||
# raise ValueError("Incorrect data format, should be YYYY-MM-DD")
|
||||
|
||||
def get_ext_doc_no_list(self, dndict=None):
|
||||
if dndict:
|
||||
dnlist = dndict['lines']
|
||||
else:
|
||||
dnlist = self.DN
|
||||
df = pd.DataFrame(dnlist)
|
||||
# print(df)
|
||||
df['RefNum']= df['No.SO/Ext.Doc.No.'].apply(lambda x: "L"+ x.split("L-")[1])
|
||||
# print(df)
|
||||
ext_doc_no=df['RefNum'].unique().tolist()
|
||||
if len(ext_doc_no)>0:
|
||||
return df, ext_doc_no
|
||||
else:
|
||||
return df, None
|
||||
|
||||
def get_open_so(self, response_string):
|
||||
QBXML = ET.fromstring(response_string)
|
||||
_OpenSalesOrderlist = []
|
||||
SalesOrderRets = QBXML.findall('.//SalesOrderRet')
|
||||
# print(f'SalesOrderRets count:{len(SalesOrderRets)}')
|
||||
for SalesOrderRet in SalesOrderRets:
|
||||
IsFullyInvoiced = SalesOrderRet.find('IsFullyInvoiced').text
|
||||
IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
|
||||
if IsFullyInvoiced=='false' and IsManuallyClosed=='false':
|
||||
_OpenSalesOrderlist.append(SalesOrderRet.find('TxnID').text)
|
||||
# RefNumber = SalesOrderRet.find('RefNumber').text
|
||||
# Memo = SalesOrderRet.find('Memo').text
|
||||
# CustomerFullName = SalesOrderRet.find('CustomerRef/FullName').text
|
||||
# TxnID = SalesOrderRet.find('TxnID').text
|
||||
# TotalAmount = SalesOrderRet.find('TotalAmount').text
|
||||
# IsFullyReceived = SalesOrderRet.find('IsFullyReceived').text
|
||||
# IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
|
||||
# print(_OpenSalesOrderlist)
|
||||
return _OpenSalesOrderlist
|
||||
|
||||
def create_open_sales_order_qbxml(self, txnid:list, IncludeLineItems=True):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
SalesOrderQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "SalesOrderQueryRq","\n " )
|
||||
if len(txnid)>0:
|
||||
for x in txnid:
|
||||
TxnID = self.create_sub_element(ET, SalesOrderQueryRq, "TxnID", x, 6 )
|
||||
else:
|
||||
return None
|
||||
if IncludeLineItems:
|
||||
IncludeLineItems = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeLineItems", 'true', 4)
|
||||
# if len(self.IncludeRetElement)>0:
|
||||
# for x in self.IncludeRetElement:
|
||||
# IncludeRetElement = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeRetElement", x, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_QBXML->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
|
||||
def get_open_sales_order(self, txnid:list):
|
||||
print(txnid, type(txnid))
|
||||
print(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
|
||||
self._get_sales_order_header(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
|
||||
|
||||
print('### SalesOrder ###')
|
||||
if __name__ == '__main__':
|
||||
starttime = timeit.default_timer()
|
||||
ini=SalesOrderQuery(FullName= '999 HPL', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'IsManuallyClosed', 'IsFullyInvoiced'])
|
||||
print(ini.create_QBXML())
|
||||
response_string = ini.connect_to_quickbooks(ini.create_QBXML())
|
||||
open_sales_orders = ini.get_open_so(response_string)
|
||||
print(open_sales_orders)
|
||||
print(ini.get_open_sales_order(open_sales_orders))
|
||||
|
||||
print("The time difference is :", timeit.default_timer() - starttime)
|
||||
544
SO_to_Inv/readSO.py
Normal file
544
SO_to_Inv/readSO.py
Normal file
@ -0,0 +1,544 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
import win32com.client
|
||||
import xmltodict
|
||||
import pprint
|
||||
import datetime
|
||||
import pandas as pd
|
||||
from datetime import date
|
||||
import timeit
|
||||
import os
|
||||
|
||||
class SalesOrderQuery:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
# print(f'kwargs:{kwargs}')
|
||||
# print(args)
|
||||
# self.SalesOrderList=[]
|
||||
self.PriceLevelName = None
|
||||
self.SPPriceLevelName = None
|
||||
self.cwd = kwargs['cwd'] if 'cwd' in kwargs else os.getcwd()
|
||||
self.item_inventory_path = "ItemInventory"
|
||||
self.price_level_filename = "PriceLevel.xlsx"
|
||||
self._df_price_level = pd.read_excel(os.path.join(self.cwd, self.item_inventory_path, self.price_level_filename), usecols=['FullName', 'PriceLevelName', 'CustomPrice'],)
|
||||
print(self._df_price_level)
|
||||
print(type(self._df_price_level.loc[(self._df_price_level['FullName']=="ECO:0:ECO-002") & (self._df_price_level['PriceLevelName']=="T 202202")].values.tolist()[0][2]))
|
||||
self.FullName = kwargs['FullName'] if 'FullName' in kwargs else None
|
||||
|
||||
self.CustomerPriceLevelName_filename = "CustomerList.xlsx"
|
||||
self._df_customer = pd.read_excel(os.path.join(self.cwd, self.item_inventory_path, self.CustomerPriceLevelName_filename), usecols=['FullName', 'PriceLevelName', 'SPName'],)
|
||||
self._df_customer = self._df_customer.fillna('')
|
||||
print(self._df_customer)
|
||||
|
||||
self.Customer = self._df_customer.loc[(self._df_customer["FullName"]==self.FullName)].values.tolist()[0]
|
||||
print(f'Customer:{self.Customer}')
|
||||
self.DN = kwargs['DN'] if 'DN' in kwargs else {}
|
||||
self.Reuse = kwargs['Reuse'] if 'Reuse' in kwargs else None
|
||||
|
||||
self.SalesOrderList= kwargs['SalesOrderList'] if 'SalesOrderList' in kwargs else []
|
||||
self.InvoiceList = None
|
||||
self.SalesOrderType = kwargs['SalesOrderType'] if 'SalesOrderType' in kwargs else 'SalesByCustomerSummary'
|
||||
self.IncludeLineItems = kwargs['IncludeLineItems'] if 'IncludeLineItems' in kwargs else 'true'
|
||||
self.IncludeRetElement = kwargs['IncludeRetElement'] if 'IncludeRetElement' in kwargs else []
|
||||
self.TxnDateRangeFilter = kwargs['TxnDateRangeFilter'] if 'TxnDateRangeFilter' in kwargs else None
|
||||
self.DateMacro = None
|
||||
if 'DateMacro' in kwargs:
|
||||
if kwargs['DateMacro'] in ['All', 'Today', 'ThisWeek', 'ThisWeekToDate', 'ThisMonth', 'ThisMonthToDate', 'ThisQuarter', 'ThisQuarterToDate', 'ThisYear', 'ThisYearToDate', 'Yesterday', 'LastWeek', 'LastWeekToDate', 'LastMonth', 'LastMonthToDate', 'LastQuarter', 'LastQuarterToDate', 'LastYear', 'LastYearToDate', 'NextWeek', 'NextFourWeeks', 'NextMonth', 'NextQuarter', 'NextYear']:
|
||||
self.DateMacro = kwargs['DateMacro']
|
||||
self.FromTxnDate = self.validate_date(kwargs['FromTxnDate']) if 'FromTxnDate' in kwargs else None
|
||||
self.ToTxnDate = self.validate_date(kwargs['ToTxnDate']) if 'ToTxnDate' in kwargs else None
|
||||
self.ReportEntityFilter = kwargs['ReportEntityFilter'] if 'ReportEntityFilter' in kwargs else None
|
||||
|
||||
# print(self.DateMacro, self.TxnDateRangeFilter, self.FromTxnDate, self.ToTxnDate)
|
||||
# print(self.Reuse)
|
||||
# if not self.Reuse:
|
||||
# self.dfDN, self.ext_doc_no_list = self.get_ext_doc_no_list(self.DN)
|
||||
# self.RefNumber = kwargs['RefNumber'] if 'RefNumber' in kwargs else self.ext_doc_no_list
|
||||
# self.get_sales_order_header()
|
||||
|
||||
def create_sub_element(self, ET, parentNode, thisNode, text="\n", whiteSpace = 0, attrib =None):
|
||||
if type(attrib) is not dict:
|
||||
attrib = {}
|
||||
ele = ET.SubElement(parentNode, thisNode)
|
||||
for x in attrib:
|
||||
ele.set(x, attrib[x])
|
||||
ele.text = text
|
||||
tail = "\n"
|
||||
for x in range(whiteSpace):
|
||||
tail = tail + " "
|
||||
ele.tail = tail
|
||||
return ele
|
||||
|
||||
def create_QBXML(self):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
SalesOrderQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "SalesOrderQueryRq","\n " )
|
||||
# SalesOrderType = self.create_sub_element(ET, SalesOrderQueryRq, 'SalesOrderType', self.SalesOrderType)
|
||||
if self.FullName:
|
||||
EntityFilter = self.create_sub_element(ET, SalesOrderQueryRq, 'EntityFilter', "\n ")
|
||||
FullName = self.create_sub_element(ET, EntityFilter, "FullName", self.FullName, 6)
|
||||
if self.DateMacro:
|
||||
TxnDateRangeFilter = self.create_sub_element(ET, SalesOrderQueryRq, "TxnDateRangeFilter", "\n ",)
|
||||
SalesOrderType = self.create_sub_element(ET, TxnDateRangeFilter, "DateMacro", self.DateMacro)
|
||||
# SalesOrderType = self.create_sub_element(ET, SalesOrderQueryRq, "DateMacro", self.DateMacro)
|
||||
|
||||
elif type(self.FromTxnDate) is datetime.date or type(self.ToTxnDate) is datetime.date:
|
||||
TxnDateRangeFilter = self.create_sub_element(ET, SalesOrderQueryRq, "TxnDateRangeFilter", "\n ",)
|
||||
if type(self.FromTxnDate) is datetime.date:
|
||||
FromTxnDate = self.create_sub_element(ET, TxnDateRangeFilter, "FromTxnDate", self.FromTxnDate.strftime('%Y-%m-%d'),4)
|
||||
if type(self.ToTxnDate) is datetime.date:
|
||||
ToTxnDate = self.create_sub_element(ET, TxnDateRangeFilter, "ToTxnDate", self.ToTxnDate.strftime('%Y-%m-%d'))
|
||||
if self.IncludeLineItems:
|
||||
IncludeLineItems = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeLineItems", self.IncludeLineItems, 4)
|
||||
if len(self.IncludeRetElement)>0:
|
||||
for x in self.IncludeRetElement:
|
||||
IncludeRetElement = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeRetElement", x, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
# print(f'create_QBXML->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
def get_customer_pricelevel(self, response_string):
|
||||
QBXML = ET.fromstring(response_string)
|
||||
PriceLevelName = QBXML.find('.//PriceLevelRef')
|
||||
if PriceLevelName:
|
||||
|
||||
PriceLevelName = PriceLevelName.find("FullName").text
|
||||
print(f'PriceLevelName:{PriceLevelName}')
|
||||
DataExtRets = QBXML.findall('.//DataExtRet')
|
||||
SP_PriceLevelName = None
|
||||
if len(DataExtRets)>0:
|
||||
for DataExtRet in DataExtRets:
|
||||
DataExtName = DataExtRet.find('DataExtName').text
|
||||
DataExtValue = DataExtRet.find('DataExtValue').text
|
||||
if DataExtName.lower() == 'special cust'.lower():
|
||||
SP_PriceLevelName = DataExtValue.upper()
|
||||
break
|
||||
self.PriceLevelName = PriceLevelName
|
||||
self.SPPriceLevelName = SP_PriceLevelName
|
||||
return PriceLevelName, SP_PriceLevelName
|
||||
|
||||
def create_customerquery_QBXML(self ):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
CustomerQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "CustomerQueryRq","\n " )
|
||||
# SalesOrderType = self.create_sub_element(ET, CustomerQueryRq, 'SalesOrderType', self.SalesOrderType)
|
||||
FullName = self.create_sub_element(ET, CustomerQueryRq, "FullName", self.FullName, 6)
|
||||
IncludeRetElement = ['FullName', 'PriceLevelRef', 'DataExtRet']
|
||||
for x in IncludeRetElement:
|
||||
IncludeRetElement = self.create_sub_element(ET, CustomerQueryRq, "IncludeRetElement", x, 4)
|
||||
OwnerID = self.create_sub_element(ET, CustomerQueryRq, "OwnerID", "0", 6)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_customer_QBXML->qbxml_query: {qbxml_query}')
|
||||
response_string=self.connect_to_quickbooks(qbxml_query)
|
||||
|
||||
return self.get_customer_pricelevel(response_string)
|
||||
return response_string
|
||||
|
||||
def create_invoiceadd_QBXML(self):
|
||||
print('create_ionvoiceadd_QBXML')
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
InvoiceAddRq = self.create_sub_element(ET, QBXMLMsgsRq, "InvoiceAddRq", "\n ",2 )
|
||||
InvoiceAdd = self.create_sub_element(ET, InvoiceAddRq, "InvoiceAdd", "\n ",4 )
|
||||
CustomerRef = self.create_sub_element(ET, InvoiceAdd, "CustomerRef", "\n ",8 )
|
||||
FullName = self.create_sub_element(ET, CustomerRef, "FullName", self.SalesOrderList[0]['CustomerFullName'], 8 )
|
||||
TemplateRef = self.create_sub_element(ET, InvoiceAdd, "TemplateRef", "\n ", 8 )
|
||||
TemplateFullName = self.create_sub_element(ET, TemplateRef, "FullName", "DBW Invoice (11%)", 8 )
|
||||
today = str(date.today())
|
||||
TxnDate = self.create_sub_element(ET, InvoiceAdd, "TxnDate", today,8) # self.DN['TxnDate'], 8 )
|
||||
|
||||
RefNumber = self.create_sub_element(ET, InvoiceAdd, "RefNumber", today,8 ) # self.DN['DNRefNum'], 8 )
|
||||
ShipDate = self.create_sub_element(ET, InvoiceAdd, "ShipDate", today,8) # self.DN['TxnDate'], 8 )
|
||||
# Memo = self.create_sub_element(ET, InvoiceAdd, "Memo", self.DN['Memo'], 10 )
|
||||
disc_amount = 0
|
||||
for soidx, salesorder in enumerate(self.SalesOrderList):
|
||||
SOTxnId = salesorder['TxnID']
|
||||
disc_amount+=int(salesorder['Disc_Amount'])
|
||||
print(f'create_invoiceadd_QBXML->SOTxnId: {SOTxnId}')
|
||||
for itemline in salesorder['SalesOrderLineRet']:
|
||||
# if 'DNQuantity' in itemline:
|
||||
InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
|
||||
# Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", str(itemline['DNQuantity'] ), 12 )
|
||||
# Quantity = self.create_sub_element(ET, InvoiceLineAdd, "Quantity", str(itemline['BackOrdered'] ), 12 )
|
||||
# UnitOfMeasure = self.create_sub_element(ET, InvoiceLineAdd, "UnitOfMeasure", str(itemline['UOM']), 12 )
|
||||
LinkToTxn = self.create_sub_element(ET, InvoiceLineAdd, "LinkToTxn", "\n ", 10 )
|
||||
TxnID = self.create_sub_element(ET, LinkToTxn, "TxnID", SOTxnId,14 )
|
||||
TxnLineID = self.create_sub_element(ET, LinkToTxn, "TxnLineID", itemline['TxnLineID'], 12 )
|
||||
if soidx == len(self.SalesOrderList)-1: #last list then set the'400_Sales_discount'
|
||||
print(f'disc_amount:{format(disc_amount, ".2f")}')
|
||||
if disc_amount != 0:
|
||||
|
||||
# disc_amount=format(disc_amount, ".2f")
|
||||
InvoiceLineAdd = self.create_sub_element(ET, InvoiceAdd, "InvoiceLineAdd", "\n ", 10 )
|
||||
ItemRef = self.create_sub_element(ET, InvoiceLineAdd, "ItemRef", "\n ", 12)
|
||||
ItemFullName = self.create_sub_element(ET, ItemRef, "FullName", "400_Sales Discount", 12)
|
||||
ItemRate = self.create_sub_element(ET, InvoiceLineAdd, "Rate", str(disc_amount), 10)
|
||||
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
# print(f'create_invoiceadd_QBXML->Create_Invoiceadd_QBXML: {qbxml_query}')
|
||||
# print(f"replyfrom qbxml:{self.connect_to_quickbooks(qbxml_query)}")
|
||||
|
||||
return qbxml_query
|
||||
|
||||
|
||||
def connect_to_quickbooks(self, qbxml_query):
|
||||
# Connect to Quickbooks
|
||||
# sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
# sessionManager.OpenConnection('', 'DASA')
|
||||
# enumfodnc= win32com.client.Dispatch('QBXMLRP2.RequestProcessor')
|
||||
# print(enumfodnc)
|
||||
# print(enumfodnc.qbFileOpenDoNotCare)
|
||||
sessionManager = win32com.client.Dispatch("QBXMLRP2.RequestProcessor")
|
||||
sessionManager.OpenConnection('', 'DASA2')
|
||||
# ticket = sessionManager.BeginSession("z:\\DBW Bogor.qbw", 2)
|
||||
|
||||
ticket = sessionManager.BeginSession("", 2)
|
||||
|
||||
# Send query and receive response
|
||||
response_string = sessionManager.ProcessRequest(ticket, qbxml_query)
|
||||
|
||||
# Disconnect from Quickbooks
|
||||
sessionManager.EndSession(ticket) # Close the company file
|
||||
sessionManager.CloseConnection() # Close the connection
|
||||
# print (f'response_string:{response_string}')
|
||||
return response_string
|
||||
|
||||
def __str__(self, *args) -> str:
|
||||
# return str(self._get_datarow(self.connect_to_quickbooks(self.create_QBXML())))
|
||||
# print("__str__")
|
||||
return str(self.get_sales_order_header())
|
||||
# return "hello"
|
||||
|
||||
def get_sales_order_header(self, *args):
|
||||
return self._get_sales_order_header(self.connect_to_quickbooks(self.create_QBXML()))
|
||||
|
||||
def status_ok(self, QBXML): #for InvoiceAddRS
|
||||
|
||||
tree = ET.fromstring(QBXML)
|
||||
|
||||
GSRQRs = tree.find(".//InvoiceAddRs")
|
||||
# print(f"GSRQRs:{GSRQRs}")
|
||||
status_code = GSRQRs.attrib #.get('statusCode')
|
||||
# print(GSRQRs.attrib)
|
||||
# print(GSRQRs.attrib['statusCode'])
|
||||
status=GSRQRs.attrib.get('statusMessage')
|
||||
|
||||
print(f'status={status}')
|
||||
if 'OK' in status:
|
||||
return True, status_code
|
||||
else:
|
||||
return False, status_code
|
||||
|
||||
|
||||
def _get_sales_order_header(self, response_string):
|
||||
print('_get_sales_order_header')
|
||||
# print(f'responsestring:{response_string}')
|
||||
QBXML = ET.fromstring(response_string)
|
||||
datadict = {}
|
||||
SalesOrderdict = {}
|
||||
_SalesOrderlist = []
|
||||
SalesOrderRets = QBXML.findall('.//SalesOrderRet')
|
||||
# print(SalesOrderRets)
|
||||
for SalesOrderRet in SalesOrderRets:
|
||||
RefNumber = SalesOrderRet.find('RefNumber').text
|
||||
# Memo = SalesOrderRet.find('Memo').text
|
||||
CustomerFullName = SalesOrderRet.find('CustomerRef/FullName').text
|
||||
TxnID = SalesOrderRet.find('TxnID').text
|
||||
TotalAmount = SalesOrderRet.find('TotalAmount').text
|
||||
IsFullyInvoiced = SalesOrderRet.find('IsFullyInvoiced').text
|
||||
IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
|
||||
# print(CustomerFullName, TxnID, TotalAmount)
|
||||
SalesOrderdict = {'RefNumber':RefNumber, 'CustomerFullName':CustomerFullName, 'TxnID':TxnID,
|
||||
'TotalAmount':TotalAmount, 'IsFullyInvoiced':IsFullyInvoiced, 'IsManuallyClosed':IsManuallyClosed, 'SalesOrderLineRet':[]}
|
||||
SalesOrderLineRet = SalesOrderRet.findall('SalesOrderLineRet')
|
||||
# print(len(SalesOrderLineRet))
|
||||
if len(SalesOrderLineRet) > 0:
|
||||
disc_amount=0
|
||||
for SalesOrderLineRet in SalesOrderLineRet:
|
||||
TxnLineID = SalesOrderLineRet.find('TxnLineID').text
|
||||
ItemFullName = SalesOrderLineRet.find('ItemRef/FullName').text
|
||||
Quantity = SalesOrderLineRet.find('Quantity').text
|
||||
UnitOfMeasure = SalesOrderLineRet.find('UnitOfMeasure').text
|
||||
Rate = float(SalesOrderLineRet.find('Rate').text)
|
||||
|
||||
Amount = float(SalesOrderLineRet.find('Amount').text)
|
||||
# if self.SPPriceLevelName:
|
||||
if self.Customer[2]:
|
||||
# print(Rate, (Rate - self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.SPPriceLevelName)].values.tolist()[0][2]))
|
||||
print(Quantity, Rate, (Rate - self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2]))
|
||||
disc_amount += float(Quantity) * (Rate-self._df_price_level.loc[(self._df_price_level['FullName']==ItemFullName) & (self._df_price_level['PriceLevelName']==self.Customer[2])].values.tolist()[0][2])
|
||||
# disc_amount+=float(Quantity)*2000 #testing only
|
||||
Invoiced = SalesOrderLineRet.find('Invoiced').text
|
||||
LineIsManuallyClosed = SalesOrderLineRet.find('IsManuallyClosed').text
|
||||
# print(TxnLineID, ItemFullName)
|
||||
BackOrdered = float(Quantity) - float(Invoiced)
|
||||
if BackOrdered:
|
||||
SalesOrderLinedict = {'TxnLineID':TxnLineID,
|
||||
'ItemFullName':ItemFullName,
|
||||
'Quantity':Quantity,
|
||||
'UOM':UnitOfMeasure,
|
||||
'Rate':Rate,
|
||||
'Amount':Amount,
|
||||
'BackOrdered':BackOrdered,
|
||||
'Invoiced':Invoiced,
|
||||
'LineIsManuallyClosed':LineIsManuallyClosed,
|
||||
}
|
||||
SalesOrderdict['SalesOrderLineRet'].append(SalesOrderLinedict)
|
||||
SalesOrderdict['Disc_Amount']=disc_amount
|
||||
_SalesOrderlist.append(SalesOrderdict)
|
||||
# print(_SalesOrderlist)
|
||||
self.SalesOrderList=_SalesOrderlist
|
||||
# print(f'_get_sales_order_header->Salesorderlist: {self.SalesOrderList}')
|
||||
return self.SalesOrderList
|
||||
|
||||
def addDiscountToInvoiceList(self, _dict:dict):
|
||||
print("addDiscountToInvoiceList")
|
||||
|
||||
|
||||
def addDNQtyToSalesOrderList(self, _dict:dict):
|
||||
_bolfoundrefnum=False
|
||||
_bol_dictisadded=False
|
||||
Error_msg = None
|
||||
for poidx, _po in enumerate(self.SalesOrderList):
|
||||
if _po['RefNumber']==_dict['RefNum']:
|
||||
_bolfoundrefnum=True
|
||||
if len(_po['SalesOrderLineRet'])>0:
|
||||
for polineidx, _poline in enumerate(_po['SalesOrderLineRet']):
|
||||
pass
|
||||
if _poline['ItemFullName']==_dict['FullName']:
|
||||
if _poline['UOM'].upper()==_dict['UOM'].upper():
|
||||
# first do UOM in _dict convert treatment
|
||||
QuantityIn_dict = _dict['Quantity']
|
||||
if _dict['UOM'].upper().startswith('ROLL_'):
|
||||
print(f"addDNQtyToSalesOrderList->DNqty:{_dict['Quantity']}, Roll_:{_dict['UOM'].split('_')[1]}")
|
||||
QuantityIn_dict = _dict['Quantity'] * int(_dict['UOM'].split("_")[1])
|
||||
pass
|
||||
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "CUTTER":
|
||||
print("addDNQtyToSalesOrderList->cutter")
|
||||
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("EB-") or _dict['Item No'].upper().startswith("TA-")):
|
||||
print("addDNQtyToSalesOrderList->LEM")
|
||||
if _dict['Item No'].split("-")[1].endswith("1006"):
|
||||
QuantityIn_dict = QuantityIn_dict * 12
|
||||
elif _dict['Item No'].split("-")[1].endswith("1025"):
|
||||
QuantityIn_dict = QuantityIn_dict * 6
|
||||
elif _dict['Item No'].split("-")[1].endswith("1100"):
|
||||
pass
|
||||
print("1100 lem")
|
||||
elif _dict['UOM'].upper() == 'BOX' and (_dict['Item No'].upper().startswith("TL-") or _dict['Item No'].upper().startswith("TFL-")):
|
||||
print("addDNQtyToSalesOrderList->Lock")
|
||||
QuantityIn_dict = QuantityIn_dict * 20
|
||||
elif _dict['UOM'].upper() == 'BOX' and _dict['Item No'].upper() == "EDG-TRIMMER":
|
||||
QuantityIn_dict = QuantityIn_dict * 12 #coz box of 12
|
||||
|
||||
|
||||
if _poline['BackOrdered']>=QuantityIn_dict:
|
||||
self.SalesOrderList[poidx]['SalesOrderLineRet'][polineidx]['DNQuantity']=float(QuantityIn_dict)
|
||||
_bol_dictisadded = True
|
||||
else:
|
||||
print(f"{_poline['ItemFullName']} BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}")
|
||||
Error_msg = f"BackOrdered < Qty in DN {_poline['BackOrdered']}<{QuantityIn_dict}"
|
||||
else:
|
||||
# print(f"UOM different {_poline['UOM']} <> {_dict['UOM']}")
|
||||
Error_msg = f"UOM different {_poline['UOM']} <> {_dict['UOM']}"
|
||||
else:
|
||||
# print("errorpoline <>DN")
|
||||
Error_msg = f"poline[ItemFullName] <> DN[FullName]; {_poline['ItemFullName']} <> {_dict['FullName']}, maybe there are 2 same namefromtaco in QB"
|
||||
else:
|
||||
pass
|
||||
# print(f"this refnum {_dict['RefNum']} have no QB PO Return Line")
|
||||
Error_msg = f"this refnum {_dict['RefNum']} have no QB PO Return Line"
|
||||
|
||||
# print (_bol_dictisadded, Error_msg)
|
||||
return _bol_dictisadded, Error_msg
|
||||
|
||||
|
||||
def prepareInvoice(self, df:pd.DataFrame = None):
|
||||
# print(df)
|
||||
_bolAllDNareOk = True
|
||||
_notindflist=[]
|
||||
_yescounter = 0
|
||||
_nocounter = 0
|
||||
if df is not None:
|
||||
_dflist = df.to_dict('records')
|
||||
# print(_dflist)
|
||||
|
||||
# else:
|
||||
# _dflist = self.dfDN.to_dict('records')
|
||||
# print(self.dfDN)
|
||||
# print(f'_dflist:{_dflist}')
|
||||
for idx, xdf in enumerate(_dflist):
|
||||
_boladdDN, _Errormsg = self.addDNQtyToSalesOrderList(xdf)
|
||||
# print(f'prepareInvoice->_Errormsg:{_Errormsg}')
|
||||
if _boladdDN:
|
||||
_dflist[idx]['ADDED']=True
|
||||
elif _Errormsg:
|
||||
_dflist[idx]['ERROR']=_Errormsg
|
||||
for xdf in (_dflist):
|
||||
if 'ADDED' not in xdf:
|
||||
# print (f"prepareInvoice->not added: {xdf['Item No']}")
|
||||
print (f"prepareInvoice->not added: {xdf}")
|
||||
|
||||
_notindflist.append(xdf)
|
||||
_nocounter+=1
|
||||
_bolAllDNareOk = False
|
||||
else:
|
||||
print (f"ADDED: {xdf['Item No']}")
|
||||
|
||||
print(f'{len(_dflist) - _nocounter} of {len(_dflist)} are added')
|
||||
return _bolAllDNareOk, _notindflist
|
||||
|
||||
|
||||
def validate_date(self, date_text):
|
||||
if date_text is None:
|
||||
return None
|
||||
try:
|
||||
return datetime.datetime.strptime(date_text, '%Y-%m-%d').date()
|
||||
except ValueError:
|
||||
return None
|
||||
# raise ValueError("Incorrect data format, should be YYYY-MM-DD")
|
||||
|
||||
def get_ext_doc_no_list(self, dndict=None):
|
||||
if dndict:
|
||||
dnlist = dndict['lines']
|
||||
else:
|
||||
dnlist = self.DN
|
||||
df = pd.DataFrame(dnlist)
|
||||
# print(df)
|
||||
df['RefNum']= df['No.SO/Ext.Doc.No.'].apply(lambda x: "L"+ x.split("L-")[1])
|
||||
# print(df)
|
||||
ext_doc_no=df['RefNum'].unique().tolist()
|
||||
if len(ext_doc_no)>0:
|
||||
return df, ext_doc_no
|
||||
else:
|
||||
return df, None
|
||||
|
||||
def get_open_so(self, response_string=None):
|
||||
if not response_string:
|
||||
response_string = self.connect_to_quickbooks(self.create_QBXML())
|
||||
|
||||
QBXML = ET.fromstring(response_string)
|
||||
_OpenSalesOrderlist = []
|
||||
SalesOrderRets = QBXML.findall('.//SalesOrderRet')
|
||||
# print(f'SalesOrderRets count:{len(SalesOrderRets)}')
|
||||
for SalesOrderRet in SalesOrderRets:
|
||||
IsFullyInvoiced = SalesOrderRet.find('IsFullyInvoiced').text
|
||||
IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
|
||||
if IsFullyInvoiced=='false' and IsManuallyClosed=='false':
|
||||
txndate = SalesOrderRet.find('TxnDate').text
|
||||
totalamount = SalesOrderRet.find('TotalAmount').text
|
||||
refnumber = SalesOrderRet.find('RefNumber').text
|
||||
|
||||
_OpenSalesOrderlist.append([SalesOrderRet.find('TxnID').text, txndate, totalamount, refnumber, ])
|
||||
# _OpenSalesOrderlist.append(SalesOrderRet.find('TxnID').text)
|
||||
# RefNumber = SalesOrderRet.find('RefNumber').text
|
||||
# Memo = SalesOrderRet.find('Memo').text
|
||||
# CustomerFullName = SalesOrderRet.find('CustomerRef/FullName').text
|
||||
# TxnID = SalesOrderRet.find('TxnID').text
|
||||
# TotalAmount = SalesOrderRet.find('TotalAmount').text
|
||||
# IsFullyReceived = SalesOrderRet.find('IsFullyReceived').text
|
||||
# IsManuallyClosed = SalesOrderRet.find('IsManuallyClosed').text
|
||||
# print(_OpenSalesOrderlist)
|
||||
return _OpenSalesOrderlist
|
||||
|
||||
def create_open_sales_order_qbxml(self, txnid:list, IncludeLineItems=True):
|
||||
root = ET.Element("QBXML")
|
||||
root.tail = "\n"
|
||||
root.text = "\n "
|
||||
QBXMLMsgsRq = ET.SubElement(root, "QBXMLMsgsRq")
|
||||
# QBXMLMsgsRq.set("onError", "continueOnError")
|
||||
QBXMLMsgsRq.set("onError", "stopOnError")
|
||||
QBXMLMsgsRq.tail = "\n"
|
||||
QBXMLMsgsRq.text = "\n "
|
||||
SalesOrderQueryRq = self.create_sub_element(ET, QBXMLMsgsRq, "SalesOrderQueryRq","\n " )
|
||||
if len(txnid)>0:
|
||||
for x in txnid:
|
||||
TxnID = self.create_sub_element(ET, SalesOrderQueryRq, "TxnID", x, 6 )
|
||||
else:
|
||||
return None
|
||||
if IncludeLineItems:
|
||||
IncludeLineItems = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeLineItems", 'true', 4)
|
||||
# if len(self.IncludeRetElement)>0:
|
||||
# for x in self.IncludeRetElement:
|
||||
# IncludeRetElement = self.create_sub_element(ET, SalesOrderQueryRq, "IncludeRetElement", x, 4)
|
||||
mydata = ET.tostring(root, encoding = "unicode")
|
||||
|
||||
qbxml_query = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||
qbxml_query = qbxml_query + """<?qbxml version="13.0"?>"""
|
||||
qbxml_query = qbxml_query + "\n" + mydata
|
||||
print(f'create_open_sale_order_qbxml->qbxml_query: {qbxml_query}')
|
||||
return qbxml_query
|
||||
|
||||
|
||||
def get_open_sales_order(self, txnlist:list):
|
||||
print(f'txnid: {txnlist}', type(txnlist))
|
||||
txnid = []
|
||||
for x in txnlist:
|
||||
if isinstance(x, list):
|
||||
txnid.append(x[0])
|
||||
else:
|
||||
txnid.append(x)
|
||||
break
|
||||
# txnid = [x[0] if isinstance(x, list) else x for x in txnid ]
|
||||
print(txnid)
|
||||
if txnid:
|
||||
# print(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
|
||||
return self._get_sales_order_header(self.connect_to_quickbooks(self.create_open_sales_order_qbxml(txnid)))
|
||||
else:
|
||||
print("There is No Open Sales Order")
|
||||
return None
|
||||
return None
|
||||
|
||||
print('### SalesOrder ###')
|
||||
if __name__ == '__main__':
|
||||
starttime = timeit.default_timer()
|
||||
# ini=SalesOrderQuery(FullName= '999 HPL', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'IsManuallyClosed', 'IsFullyInvoiced'])
|
||||
|
||||
# ini=SalesOrderQuery(FullName= 'YSM Interior', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
|
||||
ini=SalesOrderQuery(FullName= 'Abadi Serpong', IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'])
|
||||
# iya = ini.create_customerquery_QBXML() #pakai excel saja lebih cepat
|
||||
# print(iya)
|
||||
print(f'createQBXML:{ini.create_QBXML()}')
|
||||
response_string = ini.connect_to_quickbooks(ini.create_QBXML())
|
||||
# print(f'response_string:{response_string}')
|
||||
response_string = None
|
||||
open_sales_orders = ini.get_open_so()
|
||||
print(f'open sales orders:{open_sales_orders}')
|
||||
if open_sales_orders:
|
||||
|
||||
itu = ini.get_open_sales_order(open_sales_orders)
|
||||
# print(itu)
|
||||
print(f'get_open_sales_order:{itu}')
|
||||
if itu:
|
||||
|
||||
print(ini.create_invoiceadd_QBXML())
|
||||
|
||||
# ini.connect_to_quickbooks(ini.create_invoiceadd_QBXML())
|
||||
|
||||
print("The time difference is :", timeit.default_timer() - starttime)
|
||||
336
SalesOrderQuery.xml
Normal file
336
SalesOrderQuery.xml
Normal file
@ -0,0 +1,336 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?qbxml version="16.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<SalesOrderQueryRq metaData="ENUMTYPE" iterator="ENUMTYPE" iteratorID="UUIDTYPE">
|
||||
<!-- BEGIN OR -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<RefNumberCaseSensitive >STRTYPE</RefNumberCaseSensitive> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<MaxReturned >INTTYPE</MaxReturned> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ModifiedDateRangeFilter> <!-- optional -->
|
||||
<FromModifiedDate >DATETIMETYPE</FromModifiedDate> <!-- optional -->
|
||||
<ToModifiedDate >DATETIMETYPE</ToModifiedDate> <!-- optional -->
|
||||
</ModifiedDateRangeFilter>
|
||||
<!-- OR -->
|
||||
<TxnDateRangeFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<FromTxnDate >DATETYPE</FromTxnDate> <!-- optional -->
|
||||
<ToTxnDate >DATETYPE</ToTxnDate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<!-- DateMacro may have one of the following values: All, Today, ThisWeek, ThisWeekToDate, ThisMonth, ThisMonthToDate, ThisCalendarQuarter, ThisCalendarQuarterToDate, ThisFiscalQuarter, ThisFiscalQuarterToDate, ThisCalendarYear, ThisCalendarYearToDate, ThisFiscalYear, ThisFiscalYearToDate, Yesterday, LastWeek, LastWeekToDate, LastMonth, LastMonthToDate, LastCalendarQuarter, LastCalendarQuarterToDate, LastFiscalQuarter, LastFiscalQuarterToDate, LastCalendarYear, LastCalendarYearToDate, LastFiscalYear, LastFiscalYearToDate, NextWeek, NextFourWeeks, NextMonth, NextCalendarQuarter, NextCalendarYear, NextFiscalQuarter, NextFiscalYear -->
|
||||
<DateMacro >ENUMTYPE</DateMacro> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</TxnDateRangeFilter>
|
||||
<!-- END OR -->
|
||||
<EntityFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</EntityFilter>
|
||||
<!-- BEGIN OR -->
|
||||
<RefNumberFilter> <!-- optional -->
|
||||
<!-- MatchCriterion may have one of the following values: StartsWith, Contains, EndsWith -->
|
||||
<MatchCriterion >ENUMTYPE</MatchCriterion> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- required -->
|
||||
</RefNumberFilter>
|
||||
<!-- OR -->
|
||||
<RefNumberRangeFilter> <!-- optional -->
|
||||
<FromRefNumber >STRTYPE</FromRefNumber> <!-- optional -->
|
||||
<ToRefNumber >STRTYPE</ToRefNumber> <!-- optional -->
|
||||
</RefNumberRangeFilter>
|
||||
<!-- END OR -->
|
||||
<CurrencyFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- END OR -->
|
||||
</CurrencyFilter>
|
||||
<!-- END OR -->
|
||||
<IncludeLineItems >BOOLTYPE</IncludeLineItems> <!-- optional -->
|
||||
<IncludeLinkedTxns >BOOLTYPE</IncludeLinkedTxns> <!-- optional -->
|
||||
<IncludeRetElement >STRTYPE</IncludeRetElement> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional, may repeat -->
|
||||
</SalesOrderQueryRq>
|
||||
|
||||
<SalesOrderQueryRs statusCode="INTTYPE" statusSeverity="STRTYPE" statusMessage="STRTYPE" retCount="INTTYPE" iteratorRemainingCount="INTTYPE" iteratorID="UUIDTYPE">
|
||||
<SalesOrderRet> <!-- optional, may repeat -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<TimeCreated >DATETIMETYPE</TimeCreated> <!-- required -->
|
||||
<TimeModified >DATETIMETYPE</TimeModified> <!-- required -->
|
||||
<EditSequence >STRTYPE</EditSequence> <!-- required -->
|
||||
<TxnNumber >INTTYPE</TxnNumber> <!-- optional -->
|
||||
<CustomerRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerRef>
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<TemplateRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</TemplateRef>
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<BillAddress> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
<City >STRTYPE</City> <!-- optional -->
|
||||
<State >STRTYPE</State> <!-- optional -->
|
||||
<PostalCode >STRTYPE</PostalCode> <!-- optional -->
|
||||
<Country >STRTYPE</Country> <!-- optional -->
|
||||
<Note >STRTYPE</Note> <!-- optional -->
|
||||
</BillAddress>
|
||||
<BillAddressBlock> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
</BillAddressBlock>
|
||||
<ShipAddress> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
<City >STRTYPE</City> <!-- optional -->
|
||||
<State >STRTYPE</State> <!-- optional -->
|
||||
<PostalCode >STRTYPE</PostalCode> <!-- optional -->
|
||||
<Country >STRTYPE</Country> <!-- optional -->
|
||||
<Note >STRTYPE</Note> <!-- optional -->
|
||||
</ShipAddress>
|
||||
<ShipAddressBlock> <!-- optional -->
|
||||
<Addr1 >STRTYPE</Addr1> <!-- optional -->
|
||||
<Addr2 >STRTYPE</Addr2> <!-- optional -->
|
||||
<Addr3 >STRTYPE</Addr3> <!-- optional -->
|
||||
<Addr4 >STRTYPE</Addr4> <!-- optional -->
|
||||
<Addr5 >STRTYPE</Addr5> <!-- optional -->
|
||||
</ShipAddressBlock>
|
||||
<PONumber >STRTYPE</PONumber> <!-- optional -->
|
||||
<TermsRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</TermsRef>
|
||||
<DueDate >DATETYPE</DueDate> <!-- optional -->
|
||||
<SalesRepRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesRepRef>
|
||||
<FOB >STRTYPE</FOB> <!-- optional -->
|
||||
<ShipDate >DATETYPE</ShipDate> <!-- optional -->
|
||||
<ShipMethodRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ShipMethodRef>
|
||||
<Subtotal >AMTTYPE</Subtotal> <!-- optional -->
|
||||
<ItemSalesTaxRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemSalesTaxRef>
|
||||
<SalesTaxPercentage >PERCENTTYPE</SalesTaxPercentage> <!-- optional -->
|
||||
<SalesTaxTotal >AMTTYPE</SalesTaxTotal> <!-- optional -->
|
||||
<TotalAmount >AMTTYPE</TotalAmount> <!-- optional -->
|
||||
<CurrencyRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CurrencyRef>
|
||||
<ExchangeRate >FLOATTYPE</ExchangeRate> <!-- optional -->
|
||||
<TotalAmountInHomeCurrency >AMTTYPE</TotalAmountInHomeCurrency> <!-- optional -->
|
||||
<IsManuallyClosed >BOOLTYPE</IsManuallyClosed> <!-- optional -->
|
||||
<IsFullyInvoiced >BOOLTYPE</IsFullyInvoiced> <!-- optional -->
|
||||
<Memo >STRTYPE</Memo> <!-- optional -->
|
||||
<CustomerMsgRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerMsgRef>
|
||||
<IsToBePrinted >BOOLTYPE</IsToBePrinted> <!-- optional -->
|
||||
<IsToBeEmailed >BOOLTYPE</IsToBeEmailed> <!-- optional -->
|
||||
<IsTaxIncluded >BOOLTYPE</IsTaxIncluded> <!-- optional -->
|
||||
<CustomerSalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CustomerSalesTaxCodeRef>
|
||||
<Other >STRTYPE</Other> <!-- optional -->
|
||||
<ExternalGUID >GUIDTYPE</ExternalGUID> <!-- optional -->
|
||||
<LinkedTxn> <!-- optional, may repeat -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- required -->
|
||||
<!-- TxnType may have one of the following values: ARRefundCreditCard, Bill, BillPaymentCheck, BillPaymentCreditCard, BuildAssembly, Charge, Check, CreditCardCharge, CreditCardCredit, CreditMemo, Deposit, Estimate, InventoryAdjustment, Invoice, ItemReceipt, JournalEntry, LiabilityAdjustment, Paycheck, PayrollLiabilityCheck, PurchaseOrder, ReceivePayment, SalesOrder, SalesReceipt, SalesTaxPaymentCheck, Transfer, VendorCredit, YTDAdjustment -->
|
||||
<TxnType >ENUMTYPE</TxnType> <!-- required -->
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<!-- LinkType may have one of the following values: AMTTYPE, QUANTYPE -->
|
||||
<LinkType >ENUMTYPE</LinkType> <!-- optional -->
|
||||
<Amount >AMTTYPE</Amount> <!-- required -->
|
||||
</LinkedTxn>
|
||||
<!-- BEGIN OR -->
|
||||
<SalesOrderLineRet> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<!-- BEGIN OR -->
|
||||
<Rate >PRICETYPE</Rate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<RatePercent >PERCENTTYPE</RatePercent> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ExpirationDateForSerialLotNumber>STRTYPE</ExpirationDateForSerialLotNumber> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<Invoiced >QUANTYPE</Invoiced> <!-- optional -->
|
||||
<IsManuallyClosed >BOOLTYPE</IsManuallyClosed> <!-- optional -->
|
||||
<Other1 >STRTYPE</Other1> <!-- optional -->
|
||||
<Other2 >STRTYPE</Other2> <!-- optional -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</SalesOrderLineRet>
|
||||
<!-- OR -->
|
||||
<SalesOrderLineGroupRet> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemGroupRef> <!-- required -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemGroupRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<IsPrintItemsInGroup >BOOLTYPE</IsPrintItemsInGroup> <!-- required -->
|
||||
<TotalAmount >AMTTYPE</TotalAmount> <!-- required -->
|
||||
<SalesOrderLineRet> <!-- optional, may repeat -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- required -->
|
||||
<ItemRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ItemRef>
|
||||
<Desc >STRTYPE</Desc> <!-- optional -->
|
||||
<Quantity >QUANTYPE</Quantity> <!-- optional -->
|
||||
<UnitOfMeasure >STRTYPE</UnitOfMeasure> <!-- optional -->
|
||||
<OverrideUOMSetRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</OverrideUOMSetRef>
|
||||
<!-- BEGIN OR -->
|
||||
<Rate >PRICETYPE</Rate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<RatePercent >PERCENTTYPE</RatePercent> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ClassRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</ClassRef>
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<InventorySiteRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteRef>
|
||||
<InventorySiteLocationRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</InventorySiteLocationRef>
|
||||
<!-- BEGIN OR -->
|
||||
<SerialNumber >STRTYPE</SerialNumber> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<LotNumber >STRTYPE</LotNumber> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
<ExpirationDateForSerialLotNumber>STRTYPE</ExpirationDateForSerialLotNumber> <!-- optional -->
|
||||
<SalesTaxCodeRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</SalesTaxCodeRef>
|
||||
<Invoiced >QUANTYPE</Invoiced> <!-- optional -->
|
||||
<IsManuallyClosed >BOOLTYPE</IsManuallyClosed> <!-- optional -->
|
||||
<Other1 >STRTYPE</Other1> <!-- optional -->
|
||||
<Other2 >STRTYPE</Other2> <!-- optional -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</SalesOrderLineRet>
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
</SalesOrderLineGroupRet>
|
||||
<!-- END OR -->
|
||||
<DataExtRet> <!-- optional, may repeat -->
|
||||
<OwnerID >GUIDTYPE</OwnerID> <!-- optional -->
|
||||
<DataExtName >STRTYPE</DataExtName> <!-- required -->
|
||||
<!-- DataExtType may have one of the following values: AMTTYPE, DATETIMETYPE, INTTYPE, PERCENTTYPE, PRICETYPE, QUANTYPE, STR1024TYPE, STR255TYPE -->
|
||||
<DataExtType >ENUMTYPE</DataExtType> <!-- required -->
|
||||
<DataExtValue >STRTYPE</DataExtValue> <!-- required -->
|
||||
</DataExtRet>
|
||||
<FulfillmentStatus>ENUMTYPE</FulfillmentStatus> <!-- optional -->
|
||||
<ShippingDetails> <!-- optional -->
|
||||
<ShippingDetailsLineRet> <!-- optional -->
|
||||
<TrackingID>IDTYPE</TrackingID> <!-- optional -->
|
||||
<CarrierName>STRTYPE</CarrierName> <!-- optional -->
|
||||
<ShippingMethod>STRTYPE</ShippingMethod> <!-- optional -->
|
||||
<ShippingCharges>AMTTYPE</ShippingCharges> <!-- optional -->
|
||||
</ShippingDetailsLineRet>
|
||||
</ShippingDetails>
|
||||
<SOChannel>ENUMTYPE</SOChannel> <!-- optional -->
|
||||
<StoreName>STRTYPE</StoreName> <!-- optional -->
|
||||
<StoreType>STRTYPE</StoreType> <!-- optional -->
|
||||
</SalesOrderRet>
|
||||
</SalesOrderQueryRs>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
147
TransactionQuery.xml
Normal file
147
TransactionQuery.xml
Normal file
@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?qbxml version="16.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="stopOnError">
|
||||
<TransactionQueryRq metaData="ENUMTYPE" iterator="ENUMTYPE" iteratorID="UUIDTYPE">
|
||||
<!-- BEGIN OR -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<MaxReturned >INTTYPE</MaxReturned> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<RefNumberCaseSensitive >STRTYPE</RefNumberCaseSensitive> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<RefNumberFilter> <!-- optional -->
|
||||
<!-- MatchCriterion may have one of the following values: StartsWith, Contains, EndsWith -->
|
||||
<MatchCriterion >ENUMTYPE</MatchCriterion> <!-- required -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- required -->
|
||||
</RefNumberFilter>
|
||||
<!-- OR -->
|
||||
<RefNumberRangeFilter> <!-- optional -->
|
||||
<FromRefNumber >STRTYPE</FromRefNumber> <!-- optional -->
|
||||
<ToRefNumber >STRTYPE</ToRefNumber> <!-- optional -->
|
||||
</RefNumberRangeFilter>
|
||||
<!-- END OR -->
|
||||
<TransactionModifiedDateRangeFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<FromModifiedDate >DATETIMETYPE</FromModifiedDate> <!-- optional -->
|
||||
<ToModifiedDate >DATETIMETYPE</ToModifiedDate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<!-- DateMacro may have one of the following values: All, Today, ThisWeek, ThisWeekToDate, ThisMonth, ThisMonthToDate, ThisCalendarQuarter, ThisCalendarQuarterToDate, ThisFiscalQuarter, ThisFiscalQuarterToDate, ThisCalendarYear, ThisCalendarYearToDate, ThisFiscalYear, ThisFiscalYearToDate, Yesterday, LastWeek, LastWeekToDate, LastMonth, LastMonthToDate, LastCalendarQuarter, LastCalendarQuarterToDate, LastFiscalQuarter, LastFiscalQuarterToDate, LastCalendarYear, LastCalendarYearToDate, LastFiscalYear, LastFiscalYearToDate, NextWeek, NextFourWeeks, NextMonth, NextCalendarQuarter, NextCalendarYear, NextFiscalQuarter, NextFiscalYear -->
|
||||
<DateMacro >ENUMTYPE</DateMacro> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</TransactionModifiedDateRangeFilter>
|
||||
<TransactionDateRangeFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<FromTxnDate >DATETYPE</FromTxnDate> <!-- optional -->
|
||||
<ToTxnDate >DATETYPE</ToTxnDate> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<!-- DateMacro may have one of the following values: All, Today, ThisWeek, ThisWeekToDate, ThisMonth, ThisMonthToDate, ThisCalendarQuarter, ThisCalendarQuarterToDate, ThisFiscalQuarter, ThisFiscalQuarterToDate, ThisCalendarYear, ThisCalendarYearToDate, ThisFiscalYear, ThisFiscalYearToDate, Yesterday, LastWeek, LastWeekToDate, LastMonth, LastMonthToDate, LastCalendarQuarter, LastCalendarQuarterToDate, LastFiscalQuarter, LastFiscalQuarterToDate, LastCalendarYear, LastCalendarYearToDate, LastFiscalYear, LastFiscalYearToDate, NextWeek, NextFourWeeks, NextMonth, NextCalendarQuarter, NextCalendarYear, NextFiscalQuarter, NextFiscalYear -->
|
||||
<DateMacro >ENUMTYPE</DateMacro> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</TransactionDateRangeFilter>
|
||||
<TransactionEntityFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<!-- EntityTypeFilter may have one of the following values: Customer, Employee, OtherName, Vendor -->
|
||||
<EntityTypeFilter >ENUMTYPE</EntityTypeFilter> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</TransactionEntityFilter>
|
||||
<TransactionAccountFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<!-- AccountTypeFilter may have one of the following values: AccountsPayable, AccountsReceivable, AllowedFor1099, APAndSalesTax, APOrCreditCard, ARAndAP, Asset, BalanceSheet, Bank, BankAndARAndAPAndUF, BankAndUF, CostOfSales, CreditCard, CurrentAsset, CurrentAssetAndExpense, CurrentLiability, Equity, EquityAndIncomeAndExpense, ExpenseAndOtherExpense, FixedAsset, IncomeAndExpense, IncomeAndOtherIncome, Liability, LiabilityAndEquity, LongTermLiability, NonPosting, OrdinaryExpense, OrdinaryIncome, OrdinaryIncomeAndCOGS, OrdinaryIncomeAndExpense, OtherAsset, OtherCurrentAsset, OtherCurrentLiability, OtherExpense, OtherIncome, OtherIncomeOrExpense -->
|
||||
<AccountTypeFilter >ENUMTYPE</AccountTypeFilter> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</TransactionAccountFilter>
|
||||
<TransactionItemFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<!-- ItemTypeFilter may have one of the following values: AllExceptFixedAsset, Assembly, Discount, FixedAsset, Inventory, InventoryAndAssembly, NonInventory, OtherCharge, Payment, Sales, SalesTax, Service -->
|
||||
<ItemTypeFilter >ENUMTYPE</ItemTypeFilter> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</TransactionItemFilter>
|
||||
<TransactionClassFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<ListIDWithChildren >IDTYPE</ListIDWithChildren> <!-- optional -->
|
||||
<!-- OR -->
|
||||
<FullNameWithChildren >STRTYPE</FullNameWithChildren> <!-- optional -->
|
||||
<!-- END OR -->
|
||||
</TransactionClassFilter>
|
||||
<TransactionTypeFilter> <!-- optional -->
|
||||
<!-- TxnTypeFilter may have one of the following values: All, ARRefundCreditCard, Bill, BillPaymentCheck, BillPaymentCreditCard, BuildAssembly, Charge, Check, CreditCardCharge, CreditCardCredit, CreditMemo, Deposit, Estimate, InventoryAdjustment, Invoice, ItemReceipt, JournalEntry, LiabilityAdjustment, Paycheck, PayrollLiabilityCheck, PurchaseOrder, ReceivePayment, SalesOrder, SalesReceipt, SalesTaxPaymentCheck, Transfer, VendorCredit, YTDAdjustment -->
|
||||
<TxnTypeFilter >ENUMTYPE</TxnTypeFilter> <!-- required, may repeat -->
|
||||
</TransactionTypeFilter>
|
||||
<!-- TransactionDetailLevelFilter may have one of the following values: All, SummaryOnly [DEFAULT], AllExceptSummary -->
|
||||
<TransactionDetailLevelFilter >ENUMTYPE</TransactionDetailLevelFilter> <!-- optional -->
|
||||
<!-- TransactionPostingStatusFilter may have one of the following values: Either [DEFAULT], NonPosting, Posting -->
|
||||
<TransactionPostingStatusFilter >ENUMTYPE</TransactionPostingStatusFilter> <!-- optional -->
|
||||
<!-- TransactionPaidStatusFilter may have one of the following values: Either [DEFAULT], Closed, Open -->
|
||||
<TransactionPaidStatusFilter >ENUMTYPE</TransactionPaidStatusFilter> <!-- optional -->
|
||||
<CurrencyFilter> <!-- optional -->
|
||||
<!-- BEGIN OR -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional, may repeat -->
|
||||
<!-- OR -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional, may repeat -->
|
||||
<!-- END OR -->
|
||||
</CurrencyFilter>
|
||||
<!-- END OR -->
|
||||
<IncludeRetElement >STRTYPE</IncludeRetElement> <!-- optional, may repeat -->
|
||||
</TransactionQueryRq>
|
||||
|
||||
<TransactionQueryRs statusCode="INTTYPE" statusSeverity="STRTYPE" statusMessage="STRTYPE" retCount="INTTYPE" iteratorRemainingCount="INTTYPE" iteratorID="UUIDTYPE">
|
||||
<TransactionRet> <!-- optional, may repeat -->
|
||||
<!-- TxnType may have one of the following values: ARRefundCreditCard, Bill, BillPaymentCheck, BillPaymentCreditCard, BuildAssembly, Charge, Check, CreditCardCharge, CreditCardCredit, CreditMemo, Deposit, Estimate, InventoryAdjustment, Invoice, ItemReceipt, JournalEntry, LiabilityAdjustment, Paycheck, PayrollLiabilityCheck, PurchaseOrder, ReceivePayment, SalesOrder, SalesReceipt, SalesTaxPaymentCheck, Transfer, VendorCredit, YTDAdjustment -->
|
||||
<TxnType >ENUMTYPE</TxnType> <!-- optional -->
|
||||
<TxnID >IDTYPE</TxnID> <!-- optional -->
|
||||
<TxnLineID >IDTYPE</TxnLineID> <!-- optional -->
|
||||
<TimeCreated >DATETIMETYPE</TimeCreated> <!-- optional -->
|
||||
<TimeModified >DATETIMETYPE</TimeModified> <!-- optional -->
|
||||
<EntityRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</EntityRef>
|
||||
<AccountRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</AccountRef>
|
||||
<TxnDate >DATETYPE</TxnDate> <!-- optional -->
|
||||
<RefNumber >STRTYPE</RefNumber> <!-- optional -->
|
||||
<Amount >AMTTYPE</Amount> <!-- optional -->
|
||||
<CurrencyRef> <!-- optional -->
|
||||
<ListID >IDTYPE</ListID> <!-- optional -->
|
||||
<FullName >STRTYPE</FullName> <!-- optional -->
|
||||
</CurrencyRef>
|
||||
<ExchangeRate >FLOATTYPE</ExchangeRate> <!-- optional -->
|
||||
<AmountInHomeCurrency >AMTTYPE</AmountInHomeCurrency> <!-- optional -->
|
||||
<Memo >STRTYPE</Memo> <!-- optional -->
|
||||
</TransactionRet>
|
||||
</TransactionQueryRs>
|
||||
</QBXMLMsgsRq>
|
||||
</QBXML>
|
||||
BIN
database.db
Normal file
BIN
database.db
Normal file
Binary file not shown.
166
django/.gitignore
vendored
Normal file
166
django/.gitignore
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
#excel files
|
||||
*.xlsx
|
||||
*.xls
|
||||
|
||||
ItemInventory/
|
||||
0
django/Customer/__init__.py
Normal file
0
django/Customer/__init__.py
Normal file
4
django/Customer/admin.py
Normal file
4
django/Customer/admin.py
Normal file
@ -0,0 +1,4 @@
|
||||
from django.contrib import admin
|
||||
from .models import Customer
|
||||
|
||||
admin.site.register(Customer)
|
||||
6
django/Customer/apps.py
Normal file
6
django/Customer/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CustomerConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'Customer'
|
||||
7
django/Customer/forms.py
Normal file
7
django/Customer/forms.py
Normal file
@ -0,0 +1,7 @@
|
||||
from django.forms import ModelForm
|
||||
from .models import Customer
|
||||
|
||||
class CustomerForm(ModelForm):
|
||||
class Meta:
|
||||
model = Customer
|
||||
fields = "__all__"
|
||||
69
django/Customer/migrations/0001_initial.py
Normal file
69
django/Customer/migrations/0001_initial.py
Normal file
@ -0,0 +1,69 @@
|
||||
# Generated by Django 4.2 on 2023-05-02 10:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Customer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('TimeCreated', models.DateTimeField(auto_created=True)),
|
||||
('CustomerName', models.CharField(max_length=80)),
|
||||
('CustomerFullName', models.CharField(max_length=160)),
|
||||
('CustomerIsActive', models.BooleanField(default=True)),
|
||||
('Sublevel', models.PositiveSmallIntegerField(default=0)),
|
||||
('CompanyName', models.CharField(max_length=80)),
|
||||
('Salutation', models.CharField(max_length=5)),
|
||||
('FirstName', models.CharField(max_length=80)),
|
||||
('MiddleName', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('LastName', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr1', models.CharField(max_length=80)),
|
||||
('BillAddr2', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr3', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr4', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr5', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillCity', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillState', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillPostalCode', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillCountry', models.CharField(default='Indonesia', max_length=80)),
|
||||
('BillNote', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr1', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr2', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr3', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr4', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr5', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipCity', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipState', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipPostalCode', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipCountry', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipNote', models.CharField(blank=True, max_length=200, null=True)),
|
||||
('Phone', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Contact', models.CharField(max_length=80)),
|
||||
('Notes', models.CharField(blank=True, max_length=200, null=True)),
|
||||
('AltPhone', models.CharField(blank=True, max_length=25, null=True)),
|
||||
('Fax', models.CharField(blank=True, max_length=25, null=True)),
|
||||
('Email', models.CharField(blank=True, max_length=50, null=True)),
|
||||
('AltContact', models.CharField(blank=True, max_length=50, null=True)),
|
||||
('CreditLimit', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
|
||||
('NPWP', models.CharField(max_length=20)),
|
||||
('KTP', models.CharField(max_length=16)),
|
||||
('DMS_Cust_Name', models.CharField(max_length=80)),
|
||||
('DMS_Cust_Code', models.CharField(max_length=80)),
|
||||
('Special_Cust', models.BooleanField(default=False)),
|
||||
('EFaktur_Name', models.CharField(max_length=80)),
|
||||
('Efaktur_Address', models.CharField(max_length=80)),
|
||||
('Coordinates', models.CharField(blank=True, max_length=50, null=True)),
|
||||
('TimeModified', models.DateTimeField(auto_now=True)),
|
||||
('Parent', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='Customer.customer')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-02 10:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Customer', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='TimeCreated',
|
||||
field=models.DateTimeField(auto_now_add=True),
|
||||
),
|
||||
]
|
||||
19
django/Customer/migrations/0003_alter_customer_parent.py
Normal file
19
django/Customer/migrations/0003_alter_customer_parent.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2 on 2023-05-02 10:51
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Customer', '0002_alter_customer_timecreated'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='Parent',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='Customer.customer'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.2 on 2023-05-08 06:06
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0008_alter_pricelevelitem_pl'),
|
||||
('Customer', '0003_alter_customer_parent'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='customer',
|
||||
name='PriceLevelRefFullName',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='PriceList', to='Item.pricelevel'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,48 @@
|
||||
# Generated by Django 4.2 on 2023-05-13 18:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Customer', '0004_customer_pricelevelreffullname'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='Coordinates',
|
||||
field=models.CharField(blank=True, max_length=30, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='DMS_Cust_Code',
|
||||
field=models.CharField(blank=True, max_length=80, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='DMS_Cust_Name',
|
||||
field=models.CharField(blank=True, max_length=80, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='EFaktur_Name',
|
||||
field=models.CharField(blank=True, max_length=80, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='Efaktur_Address',
|
||||
field=models.CharField(blank=True, max_length=80, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='KTP',
|
||||
field=models.CharField(blank=True, max_length=16, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='NPWP',
|
||||
field=models.CharField(blank=True, max_length=20, null=True),
|
||||
),
|
||||
]
|
||||
18
django/Customer/migrations/0006_alter_customer_salutation.py
Normal file
18
django/Customer/migrations/0006_alter_customer_salutation.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-19 15:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Customer', '0005_alter_customer_coordinates_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='Salutation',
|
||||
field=models.CharField(choices=[('Mr', 'Mr.'), ('Mrs', 'Mrs.')], max_length=4),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-08-31 10:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Customer', '0006_alter_customer_salutation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='CustomerFullName',
|
||||
field=models.CharField(max_length=160, unique=True),
|
||||
),
|
||||
]
|
||||
0
django/Customer/migrations/__init__.py
Normal file
0
django/Customer/migrations/__init__.py
Normal file
97
django/Customer/models.py
Normal file
97
django/Customer/models.py
Normal file
@ -0,0 +1,97 @@
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from Item.models import PriceLevel
|
||||
|
||||
class Customer(models.Model):
|
||||
MR = 'Mr'
|
||||
MRS = 'Mrs'
|
||||
|
||||
SALUTATION_CHOICE = [
|
||||
(MR, 'Mr.'),
|
||||
(MRS, 'Mrs.'),
|
||||
]
|
||||
|
||||
CustomerName = models.CharField(max_length=80)
|
||||
CustomerFullName = models.CharField(max_length=160, unique=True)
|
||||
CustomerIsActive = models.BooleanField(default=True)
|
||||
Parent = models.ForeignKey("Customer", on_delete=models.DO_NOTHING, blank=True, null=True)
|
||||
Sublevel = models.PositiveSmallIntegerField(default=0)
|
||||
CompanyName = models.CharField(max_length=80)
|
||||
Salutation = models.CharField(max_length=4, choices=SALUTATION_CHOICE)
|
||||
FirstName = models.CharField(max_length=80)
|
||||
MiddleName = models.CharField(max_length=80, blank=True, null=True)
|
||||
LastName = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillAddr1 = models.CharField(max_length=80)
|
||||
BillAddr2 = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillAddr3 = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillAddr4 = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillAddr5 = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillCity = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillState = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillPostalCode = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillCountry = models.CharField(max_length=80, default="Indonesia")
|
||||
BillNote = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipAddr1 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipAddr2 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipAddr3 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipAddr4 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipAddr5 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipCity = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipState = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipPostalCode = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipCountry = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipNote = models.CharField(max_length=200, blank=True, null=True)
|
||||
Phone = models.CharField(max_length=80, blank=True, null=True)
|
||||
Contact = models.CharField(max_length=80)
|
||||
# CustomerTypeRefFullName
|
||||
# TermsRefFullName
|
||||
# SalesRepRefFullName
|
||||
# Balance
|
||||
# TotalBalance
|
||||
# SalesTaxCodeRefFullName
|
||||
# ItemSalesTaxRefFullName
|
||||
# CreditCardNo
|
||||
# ExpirationMonth
|
||||
# ExpirationYear
|
||||
# NameOnCard
|
||||
# CreditCardAddress
|
||||
# CreditCardPostalCode
|
||||
# JobStatus
|
||||
# JobStartDate
|
||||
# JobProjectedEndDate
|
||||
# JobEndDate
|
||||
# JobTypeRefFullName
|
||||
Notes = models.CharField(max_length=200, blank=True, null=True)
|
||||
PriceLevelRefFullName = models.ForeignKey("Item.PriceLevel", related_name="PriceList", on_delete=models.SET_NULL, blank=True, null=True)
|
||||
# CurrencyRefFullName
|
||||
AltPhone = models.CharField(max_length=25, blank=True, null=True)
|
||||
Fax = models.CharField(max_length=25, blank=True, null=True)
|
||||
Email = models.CharField(max_length=50, blank=True, null=True)
|
||||
AltContact = models.CharField(max_length=50, blank=True, null=True)
|
||||
# ResaleNumber
|
||||
# AccountNumber
|
||||
CreditLimit = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True)
|
||||
# PreferredPaymentMethodRefFullName
|
||||
NPWP = models.CharField(max_length=20, blank=True, null=True)
|
||||
KTP = models.CharField(max_length=16, blank=True, null=True)
|
||||
DMS_Cust_Name = models.CharField(max_length=80, blank=True, null=True)
|
||||
DMS_Cust_Code = models.CharField(max_length=80, blank=True, null=True)
|
||||
Special_Cust = models.BooleanField(default=False)
|
||||
EFaktur_Name = models.CharField(max_length=80, blank=True, null=True)
|
||||
Efaktur_Address = models.CharField(max_length=80, blank=True, null=True)
|
||||
Coordinates = models.CharField(max_length=30, blank=True, null=True)
|
||||
TimeCreated = models.DateTimeField(auto_now_add=True)
|
||||
TimeModified = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.CustomerName}; {self.PriceLevelRefFullName}"
|
||||
|
||||
def get_field_name(obj):
|
||||
# return [(f.name, f.value_to_string(obj)) for f in obj._meta.fields] #get value convert it to string
|
||||
return [(f.verbose_name, f.name, f.value_from_object(obj)) for f in obj._meta.fields]
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('Customer:edit_customer', args=[str(self.id)])
|
||||
|
||||
|
||||
|
||||
12
django/Customer/templates/Customer/addedit.html
Normal file
12
django/Customer/templates/Customer/addedit.html
Normal file
@ -0,0 +1,12 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% block title %}{{title}}{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<form method="POST" action="">
|
||||
{% csrf_token %}
|
||||
{{form|crispy}}
|
||||
{% comment %} {{form.CustomerName|as_crispy_field}} {% endcomment %}
|
||||
<button type="submit">submit</button>
|
||||
</form>
|
||||
{% endblock body %}
|
||||
62
django/Customer/templates/Customer/index copy.html
Normal file
62
django/Customer/templates/Customer/index copy.html
Normal file
@ -0,0 +1,62 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}test obj{% endblock title %}
|
||||
|
||||
{% block search_nav %}<form class="d-flex" role="search" action="" method="GET">
|
||||
<input onfocusout="focusout(this)" id="source" list="itemname" class="form-control me-2" type="search" placeholder="Search" aria-label="Search" name="q">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
{% endblock search_nav %}
|
||||
|
||||
{% block body %}
|
||||
{% comment %} {{objects}} {% endcomment %}
|
||||
{{objects.count}}
|
||||
<datalist id="itemname">
|
||||
{% for obj in objects %}
|
||||
{% comment %} {{obj.0}} {% endcomment %}
|
||||
{% if obj.itempricelevel__Price %}
|
||||
<option value="{{obj.FullName}}" data-price="{{obj.itempricelevel__Price}}"></option>
|
||||
{% else %}
|
||||
<option value="{{obj.FullName}}" data-price="{{obj.SalesPrice}}"></option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</datalist>
|
||||
{% comment %} {% for obj in objects %}
|
||||
{{obj}}
|
||||
{% endfor %} {% endcomment %}
|
||||
<div>
|
||||
<input id="priceid" class="form-control me-2" value = "0">
|
||||
|
||||
</div>
|
||||
|
||||
<div id="result">
|
||||
test
|
||||
</div>
|
||||
<script>
|
||||
|
||||
const source = document.getElementById('source');
|
||||
const result = document.getElementById('result');
|
||||
const itemname = document.getElementById('itemname');
|
||||
function focusout(e){
|
||||
let Text = document.querySelector('option[value="' + e.value + '"]');
|
||||
if (Text){
|
||||
console.log(e.value);
|
||||
console.log(Text.getAttribute('data-price'));
|
||||
document.getElementById('priceid').value = Text.getAttribute('data-price');
|
||||
}
|
||||
else {
|
||||
console.log("element not exist");
|
||||
document.getElementById('priceid').value = 0;
|
||||
}
|
||||
|
||||
}
|
||||
const inputHandler = function(e) {
|
||||
console.log(e.target.Text);
|
||||
result.innerHTML = e.target.value;
|
||||
}
|
||||
|
||||
source.addEventListener('input', inputHandler);
|
||||
source.addEventListener('propertychange', inputHandler);
|
||||
</script>
|
||||
|
||||
{% endblock body %}
|
||||
1
django/Customer/templates/Customer/index.html
Normal file
1
django/Customer/templates/Customer/index.html
Normal file
@ -0,0 +1 @@
|
||||
{% include 'Item/table_index.html' with title="Customer Page" %}
|
||||
3
django/Customer/tests.py
Normal file
3
django/Customer/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
9
django/Customer/urls.py
Normal file
9
django/Customer/urls.py
Normal file
@ -0,0 +1,9 @@
|
||||
from django.urls import path, include
|
||||
from .views import index, edit_customer, add_customer, delete_customer
|
||||
app_name = "Customer"
|
||||
urlpatterns = [
|
||||
path('', index, name="index" ),
|
||||
path('edit/<int:pk>', edit_customer, name="edit_customer" ),
|
||||
path('add/', add_customer, name="add_customer" ),
|
||||
path('delete/<int:pk>', delete_customer, name="delete_customer" ),
|
||||
]
|
||||
77
django/Customer/views.py
Normal file
77
django/Customer/views.py
Normal file
@ -0,0 +1,77 @@
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.urls import reverse
|
||||
from .models import Customer
|
||||
from django.db.models import Q
|
||||
from django.core.paginator import Paginator
|
||||
# from Item.models import Item, PriceLevel, PriceLevelItem
|
||||
from .forms import CustomerForm
|
||||
|
||||
# def index(request):
|
||||
# context={}
|
||||
# qs1 = Item.objects.filter(itempricelevel__PL__Name='B2020',).prefetch_related("itempricelevel").values('FullName','SalesPrice', 'itempricelevel__Price')
|
||||
# qs2 = Item.objects.exclude(itempricelevel__PL__Name='B2020').prefetch_related("itempricelevel").values( "FullName", "SalesPrice", "AlwaysNull")
|
||||
# print(qs2)
|
||||
# qs = qs1.union(qs2)
|
||||
# print(type(qs))
|
||||
# print(qs)
|
||||
# context['objects'] = qs
|
||||
# return render(request, "Customer/index.html", context=context)
|
||||
|
||||
def index(request):
|
||||
context={}
|
||||
search = request.GET.get("q")
|
||||
customers=Customer.objects.all().order_by('CustomerFullName')
|
||||
print(customers)
|
||||
if search:
|
||||
customer = Customer.objects.order_by('CustomerFullName').filter(Q(CustomerName__icontains=search) | Q(CustomerFullName__icontains=search))
|
||||
else:
|
||||
customer = Customer.objects.order_by('CustomerFullName')
|
||||
paginator = Paginator(customer, 25) # Show 25 contacts per page.
|
||||
page_number = request.GET.get("page")
|
||||
page_obj = paginator.get_page(page_number)
|
||||
# heads = [f.name for f in Customer._meta.get_fields()]
|
||||
# print(heads)
|
||||
print(customer)
|
||||
print(page_obj[0].CustomerName)
|
||||
context['objects'] = page_obj
|
||||
context['customers'] = customers
|
||||
context['addurl'] = reverse('Customer:add_customer')
|
||||
return render(request, "Customer/index.html", context=context)
|
||||
|
||||
def edit_customer(request, pk):
|
||||
print("edit customer")
|
||||
print(pk)
|
||||
print(request)
|
||||
customer = get_object_or_404(Customer, pk=pk)
|
||||
form = CustomerForm( request.POST or None, instance=customer)
|
||||
if request.method == "GET":
|
||||
print("GET")
|
||||
if customer:
|
||||
# form = CustomerForm(instance=customer)
|
||||
return render(request, "Customer/addedit.html", {"objects": [customer,], "form":form})
|
||||
elif request.method == "POST":
|
||||
print("POST")
|
||||
# form = CustomerForm(request.POST, instance=customer)
|
||||
if form.is_valid():
|
||||
print("form is valid")
|
||||
customer=form.save()
|
||||
# return redirect(reverse('Customer:edit_customer', kwargs={"pk":pk}))
|
||||
return render(request, "Customer/index.html", {"objects": [customer]})
|
||||
else:
|
||||
print("not valid form")
|
||||
return render(request, "Customer/addedit.html", {"objects": [customer,], "form":form})
|
||||
|
||||
def add_customer(request):
|
||||
print("add customer")
|
||||
form = CustomerForm(request.POST or None)
|
||||
if request.POST:
|
||||
# print(form["CustomerName"].value())
|
||||
if form.is_valid():
|
||||
# print(form.cleaned_data["CustomerName"])
|
||||
form.save()
|
||||
return redirect(reverse("Customer:index"))
|
||||
# print(form["CustomerName"].value())
|
||||
return render(request, "Customer/addedit.html", {"form":form})
|
||||
|
||||
def delete_customer(request, pk):
|
||||
print("delete customer")
|
||||
0
django/Inventory/__init__.py
Normal file
0
django/Inventory/__init__.py
Normal file
16
django/Inventory/asgi.py
Normal file
16
django/Inventory/asgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for Inventory project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Inventory.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
143
django/Inventory/settings.py
Normal file
143
django/Inventory/settings.py
Normal file
@ -0,0 +1,143 @@
|
||||
"""
|
||||
Django settings for Inventory project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 4.2.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-u(al(30azswvgyf($b)cz-%h$8hl@o&i9glc9iv)!ikopl1!s-'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'debug_toolbar',
|
||||
"crispy_forms",
|
||||
"crispy_bootstrap5",
|
||||
|
||||
"crispy_bootstrap4",
|
||||
'Item',
|
||||
'SalesOrder',
|
||||
'Invoice',
|
||||
'Customer',
|
||||
'django.contrib.humanize',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'Inventory.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'Inventory.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'Asia/Jakarta'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
INTERNAL_IPS = [
|
||||
'127.0.0.1',
|
||||
]
|
||||
|
||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||
|
||||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||
33
django/Inventory/urls.py
Normal file
33
django/Inventory/urls.py
Normal file
@ -0,0 +1,33 @@
|
||||
"""
|
||||
URL configuration for Inventory project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/4.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
# path('', include("SalesOrder.urls")),
|
||||
path('customer/', include("Customer.urls")),
|
||||
path('item/', include("Item.urls")),
|
||||
path('so/', include("SalesOrder.urls")),
|
||||
path('inv/', include("Invoice.urls")),
|
||||
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
import debug_toolbar
|
||||
urlpatterns += path('__debug__/', include(debug_toolbar.urls)),
|
||||
16
django/Inventory/wsgi.py
Normal file
16
django/Inventory/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for Inventory project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Inventory.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
0
django/Invoice/__init__.py
Normal file
0
django/Invoice/__init__.py
Normal file
3
django/Invoice/admin.py
Normal file
3
django/Invoice/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
django/Invoice/apps.py
Normal file
6
django/Invoice/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class InvoiceConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'Invoice'
|
||||
54
django/Invoice/forms.py
Normal file
54
django/Invoice/forms.py
Normal file
@ -0,0 +1,54 @@
|
||||
from django.forms import ModelForm
|
||||
from django import forms
|
||||
from .models import Invoice, InvoiceItemLine
|
||||
from crispy_forms.helper import FormHelper
|
||||
|
||||
class InvoiceForm(ModelForm):
|
||||
class Meta:
|
||||
model = Invoice
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
'TxnDate' : forms.DateInput(attrs={'type':"date"}),
|
||||
'BillAddr1' : forms.Textarea(attrs={'rows':7, 'style':'height:180px'}),
|
||||
'ShipAddr1' : forms.Textarea(attrs={'rows':7, 'style':'height:180px'}),
|
||||
'TotalAmount' : forms.TextInput(attrs={'class':'text-end hidden', 'onkeypress':'return event.preventDefault()'})
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(InvoiceForm, self).__init__(*args, **kwargs)
|
||||
self.helper = FormHelper(self)
|
||||
# self.helper.form_show_labels = False
|
||||
self.fields['TotalAmount'].label = False
|
||||
self.fields['TotalAmount'].field_class = ''
|
||||
|
||||
class InvoiceItemLineForm(ModelForm):
|
||||
class Meta:
|
||||
model = InvoiceItemLine
|
||||
fields = ('ItemRefFullName', 'Desc', 'Quantity', 'UnitOfMeasure', 'Rate', 'Amount', 'Invoiced', 'LineIsManuallyClosed')
|
||||
# fields = "__all__"
|
||||
widgets = {
|
||||
# 'ItemRefFullName' : forms.Select(choices=[('1', '1')]),
|
||||
'ItemRefFullName' : forms.NumberInput(attrs={'class':'hidden itemreffullname'}),
|
||||
'Desc' : forms.TextInput(attrs={'class':'desc'}),
|
||||
'Quantity' : forms.TextInput(attrs={'class':'quantity','onkeypress':'numberOnly(event)'}), #'onkeypress':'return (event.charCode >= 48 && event.charCode <= 57) || event.charCode == 46'}),
|
||||
'UnitOfMeasure' : forms.Select(attrs={'class':'unitofmeasure'}),
|
||||
'Rate' : forms.TextInput(attrs={'class':'rate text-end', 'onkeypress':'numberOnly(event)'}),
|
||||
'Amount' : forms.TextInput(attrs={'class':'amount text-end', 'onkeypress':'numberOnly(event)', 'onchange':'amountchanged(event)'}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(InvoiceItemLineForm, self).__init__(*args, **kwargs)
|
||||
self.helper = FormHelper(self)
|
||||
self.helper.form_show_labels = False
|
||||
# self.helper.form_class = "abc"
|
||||
# self.helper.field_class = "xyz"
|
||||
|
||||
|
||||
|
||||
DEMO_CHOICES =(
|
||||
("1", "Naveen"),
|
||||
("2", "Pranav"),
|
||||
("3", "Isha"),
|
||||
("4", "Saloni"),
|
||||
)
|
||||
class GeeksForm(forms.Form):
|
||||
geeks_field = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices = DEMO_CHOICES)
|
||||
83
django/Invoice/migrations/0001_initial.py
Normal file
83
django/Invoice/migrations/0001_initial.py
Normal file
@ -0,0 +1,83 @@
|
||||
# Generated by Django 4.2 on 2023-08-31 10:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('Customer', '0007_alter_customer_customerfullname'),
|
||||
('Item', '0010_alter_pricelevelitem_price'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Invoice',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('TxnDate', models.DateField()),
|
||||
('RefNumber', models.CharField(max_length=30, verbose_name='S.O. No.')),
|
||||
('BillAddr1', models.CharField(max_length=80, verbose_name='Name/Address')),
|
||||
('BillAddr2', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr3', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr4', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr5', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillCity', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillState', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillPostalCode', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillCountry', models.CharField(blank=True, default='Indonesia', max_length=80, null=True)),
|
||||
('BillNote', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr1', models.CharField(blank=True, max_length=80, null=True, verbose_name='Ship To')),
|
||||
('ShipAddr2', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr3', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr4', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr5', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipCity', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipState', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipPostalCode', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipCountry', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipNote', models.CharField(blank=True, max_length=200, null=True)),
|
||||
('PONumber', models.CharField(blank=True, max_length=30, null=True, verbose_name='PO. No.')),
|
||||
('TermsRefFullName', models.CharField(blank=True, max_length=10, null=True, verbose_name='Terms')),
|
||||
('SalesRepRefFullName', models.CharField(blank=True, max_length=10, null=True, verbose_name='REP')),
|
||||
('ShipDate', models.DateTimeField(blank=True, null=True)),
|
||||
('DueDate', models.DateField(blank=True, null=True)),
|
||||
('TotalAmount', models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True)),
|
||||
('CustomerMsgRefFullName', models.CharField(blank=True, max_length=120, null=True)),
|
||||
('IsToBePrinted', models.BooleanField(default=False)),
|
||||
('IsToBeEmailed', models.BooleanField(default=False)),
|
||||
('IsManuallyClosed', models.BooleanField(default=False)),
|
||||
('IsFullyInvoiced', models.BooleanField(default=False)),
|
||||
('Memo', models.CharField(blank=True, max_length=120, null=True)),
|
||||
('NPWP', models.CharField(blank=True, max_length=20, null=True)),
|
||||
('KTP', models.CharField(blank=True, max_length=16, null=True)),
|
||||
('DMS_Cust_Name', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('DMS_Cust_Code', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Special_Cust', models.BooleanField(default=False)),
|
||||
('EFaktur_Name', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Efaktur_Address', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Coordinates', models.CharField(blank=True, max_length=30, null=True)),
|
||||
('TimeCreated', models.DateTimeField(auto_now_add=True)),
|
||||
('TimeModified', models.DateTimeField(auto_now=True)),
|
||||
('CustomerRefFullName', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='Customer.customer')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InvoiceItemLine',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('Desc', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Quantity', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)),
|
||||
('Rate', models.DecimalField(blank=True, decimal_places=2, max_digits=11, null=True)),
|
||||
('Amount', models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True)),
|
||||
('Invoiced', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)),
|
||||
('LineIsManuallyClosed', models.BooleanField(blank=True, default=False, null=True)),
|
||||
('Invoice', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='Invoice.invoice')),
|
||||
('ItemRefFullName', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='Item.item')),
|
||||
('UnitOfMeasure', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='Item.uom', verbose_name='UOM')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
django/Invoice/migrations/__init__.py
Normal file
0
django/Invoice/migrations/__init__.py
Normal file
135
django/Invoice/models.py
Normal file
135
django/Invoice/models.py
Normal file
@ -0,0 +1,135 @@
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from Item.models import Item, UOM
|
||||
from Customer.models import Customer
|
||||
|
||||
class Invoice(models.Model):
|
||||
CustomerRefFullName = models.ForeignKey(Customer, on_delete=models.PROTECT)
|
||||
# TxnNumber
|
||||
# ClassRefFullName
|
||||
# TemplateRefFullName
|
||||
TxnDate = models.DateField()
|
||||
RefNumber = models.CharField(max_length=30, verbose_name="S.O. No.")
|
||||
BillAddr1 = models.CharField(max_length=80, verbose_name="Name/Address")
|
||||
BillAddr2 = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillAddr3 = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillAddr4 = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillAddr5 = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillCity = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillState = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillPostalCode = models.CharField(max_length=80, blank=True, null=True)
|
||||
BillCountry = models.CharField(max_length=80, default="Indonesia", blank=True, null=True)
|
||||
BillNote = models.CharField(max_length=80, blank=True, null=True)
|
||||
# Addr1 =
|
||||
# Addr2
|
||||
# Addr3
|
||||
ShipAddr1 = models.CharField(max_length=80, verbose_name="Ship To", blank=True, null=True)
|
||||
ShipAddr2 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipAddr3 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipAddr4 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipAddr5 = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipCity = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipState = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipPostalCode = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipCountry = models.CharField(max_length=80, blank=True, null=True)
|
||||
ShipNote = models.CharField(max_length=200, blank=True, null=True)
|
||||
|
||||
# ShipAddr1
|
||||
# ShipAddr2
|
||||
# ShipAddr3
|
||||
# ShipAddr4
|
||||
# ShipCity
|
||||
# ShipState
|
||||
# ShipPostalCode
|
||||
# ShipCountry
|
||||
# City
|
||||
# State
|
||||
# PostalCode
|
||||
# Country_Note
|
||||
PONumber = models.CharField(max_length=30, verbose_name="PO. No.", blank=True, null=True)
|
||||
TermsRefFullName = models.CharField(max_length=10, verbose_name="Terms", blank=True, null=True)
|
||||
SalesRepRefFullName = models.CharField(max_length=10, verbose_name="REP", blank=True, null=True)
|
||||
ShipDate = models.DateTimeField(blank=True, null=True)
|
||||
# ShipMethodRefFullName =
|
||||
DueDate = models.DateField(blank=True, null=True)
|
||||
# Subtotal = models.DecimalField(max_digits=14, decimal_places=2, blank=True, null=True)
|
||||
|
||||
# ItemSalesTaxRefFullName
|
||||
# SalesTaxPercentage
|
||||
# SalesTaxTotal
|
||||
# CurrencyRefFullName
|
||||
TotalAmount = models.DecimalField(max_digits=14, decimal_places=2, blank=True, null=True )
|
||||
# ExchangeRate
|
||||
# TotalAmountInHomeCurrency
|
||||
CustomerMsgRefFullName = models.CharField(max_length=120, blank=True, null=True)
|
||||
IsToBePrinted = models.BooleanField(default=False)
|
||||
IsToBeEmailed = models.BooleanField(default=False)
|
||||
# CustomerSalesTaxCodeRefFullName
|
||||
# Other
|
||||
# FOB
|
||||
IsManuallyClosed = models.BooleanField(default=False)
|
||||
IsFullyInvoiced = models.BooleanField(default=False)
|
||||
Memo = models.CharField(max_length=120, blank=True, null=True)
|
||||
# LinkedTxnID
|
||||
# TxnLineID = models.ForeignKey(InvoiceItemLine, ondelete=models.DO_NOTHING)
|
||||
|
||||
NPWP = models.CharField(max_length=20, blank=True, null=True)
|
||||
KTP = models.CharField(max_length=16, blank=True, null=True)
|
||||
DMS_Cust_Name = models.CharField(max_length=80, blank=True, null=True)
|
||||
DMS_Cust_Code = models.CharField(max_length=80, blank=True, null=True)
|
||||
Special_Cust = models.BooleanField(default=False)
|
||||
EFaktur_Name = models.CharField(max_length=80, blank=True, null=True)
|
||||
Efaktur_Address = models.CharField(max_length=80, blank=True, null=True)
|
||||
Coordinates = models.CharField(max_length=30, blank=True, null=True)
|
||||
TimeCreated = models.DateTimeField(auto_now_add=True)
|
||||
TimeModified = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.RefNumber
|
||||
|
||||
def get_field_name(obj):
|
||||
# return [(f.name, f.value_to_string(obj)) for f in obj._meta.fields] #get value convert it to string
|
||||
return [(f.verbose_name, f.name, f.value_from_object(obj)) for f in obj._meta.fields]
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('Invoice:edit_so', args=[str(self.id)])
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class InvoiceItemLine(models.Model):
|
||||
Invoice = models.ForeignKey(Invoice, on_delete=models.PROTECT)
|
||||
ItemRefFullName = models.ForeignKey(Item, on_delete=models.PROTECT)
|
||||
Desc = models.CharField(max_length=80, blank=True, null=True)
|
||||
Quantity = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
|
||||
UnitOfMeasure = models.ForeignKey(UOM, verbose_name="UOM", on_delete=models.DO_NOTHING, blank=True, null=True)
|
||||
# OverrideUOMFullName
|
||||
Rate = models.DecimalField(max_digits=11, decimal_places=2, blank=True, null=True)
|
||||
Amount = models.DecimalField(max_digits=14, decimal_places=2, blank=True, null=True)
|
||||
# InventorySiteRefFullName
|
||||
# SerialNumber
|
||||
# LotNumber
|
||||
# SalesTaxCodeRefFullName
|
||||
# Other1
|
||||
# Other2
|
||||
Invoiced = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True) #how many qty that has invoiced
|
||||
LineIsManuallyClosed = models.BooleanField(default=False, blank=True, null=True)
|
||||
# LineClass
|
||||
# GroupTxnLineID
|
||||
# GroupItemFullName
|
||||
# GroupDesc
|
||||
# GroupQuantity
|
||||
# IsPrintItemsInGroup
|
||||
# RatePercent
|
||||
# GroupLineTxnLineID
|
||||
# GroupLineItemFullName
|
||||
# GroupLineDesc
|
||||
# GroupLineQuantity
|
||||
# GroupLineUOM
|
||||
# GroupLineOverrideUOM
|
||||
# GroupLineRate
|
||||
# GroupLineAmount
|
||||
# GroupLineServiceDate
|
||||
# GroupLineSalesTaxCodeFullName
|
||||
7
django/Invoice/static/bootstrap.bundle.min.js
vendored
Normal file
7
django/Invoice/static/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
django/Invoice/static/bootstrap.min.css
vendored
Normal file
7
django/Invoice/static/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
525
django/Invoice/templates/Invoice/create-update.html
Normal file
525
django/Invoice/templates/Invoice/create-update.html
Normal file
@ -0,0 +1,525 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load humanize %}
|
||||
{% block title %}Invoice{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
{% comment %} {% if messages %}
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{message}}
|
||||
</div>
|
||||
{% endif %} {% endcomment %}
|
||||
{% if messages %}
|
||||
<ul class="messages">
|
||||
{% for message in messages %}
|
||||
<li{% if message.tags %} class="alert alert-{{ message.tags }}"{% endif %}>{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<div class="container-fluid">
|
||||
<form action="" method="POST" class="card border-info-subtle p-3 mt-2" autocomplete="off" id="form">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="col-6">{{form.CustomerRefFullName|as_crispy_field}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm">{{form.BillAddr1|as_crispy_field}}</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col-sm">{{form.TxnDate|as_crispy_field}}</div>
|
||||
<div class="col">{{form.RefNumber|as_crispy_field}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
<div class="col">{{form.PONumber|as_crispy_field}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">{{form.SalesRepRefFullName|as_crispy_field}}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col">{{form.ShipAddr1|as_crispy_field}}</div>
|
||||
</div>
|
||||
{{form.TotalAmount}}
|
||||
|
||||
{% comment %} <div >{{form.IsToBePrinted.as_hidden}}</div>
|
||||
<div >{{form.IsToBeEmailed.as_hidden}}</div>
|
||||
<div >{{form.IsManuallyClosed.as_hidden}}</div> {% endcomment %}
|
||||
<div >{{form.KTP.as_hidden}}</div>
|
||||
<div >{{form.NPWP.as_hidden}}</div>
|
||||
<div >{{form.DMS_Cust_Name.as_hidden}}</div>
|
||||
<div >{{form.DMS_Cust_Code.as_hidden}}</div>
|
||||
<div >{{form.Special_Cust.as_hidden}}</div>
|
||||
<div >{{form.EFaktur_Name.as_hidden}}</div>
|
||||
<div >{{form.Efaktur_Address.as_hidden}}</div>
|
||||
<div class="col">{{form.Coordinates.as_hidden}}</div>
|
||||
<hr style="border: 1px solid #007bff">
|
||||
|
||||
|
||||
{% for hidden in form.hidden_fields %}
|
||||
{{ hidden }}
|
||||
{% endfor %}
|
||||
|
||||
{% if formset %}
|
||||
{{ formset.management_form }}
|
||||
<div class="row">
|
||||
<div class="col-2 text-center mb-1">ItemRefFullName*</div>
|
||||
<div class="col-3 text-center">Description</div>
|
||||
<div class="col-1 text-center">Qty</div>
|
||||
<div class="col-1 text-center">UOM</div>
|
||||
<div class="col-2 text-center">Rate</div>
|
||||
<div class="col">
|
||||
<div class='row'>
|
||||
<div class='col-10 text-end'>
|
||||
Amount</div>
|
||||
<div class='col text-end'>Del</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id='itemline-form-list'>
|
||||
{% comment %} {% for form in formset %} {% endcomment %}
|
||||
{% for qs, form in qsformset %}
|
||||
<div class='itemline-form'>
|
||||
{{form.id}}
|
||||
{% comment %} {{form.ItemRefFullName.field}} {% endcomment %}
|
||||
<div class="row parentrow">
|
||||
<div class="col-2">
|
||||
<div id='div_id_form-{{forloop.counter0}}-inputitem' class='mb-1'>
|
||||
{% comment %} <label for="id_form-{{forloop.counter0}}-inputitem" class="form-label requiredField"> ItemRefFullName<span class="asteriskField">*</span>
|
||||
</label> {% endcomment %}
|
||||
<div></div>
|
||||
<input type='text' list='itemname' onchange='changeitem(event)' value='{{qs.ItemRefFullName}}' class='textinput form-control inputitem'
|
||||
id='id_form-{{forloop.counter0}}-inputitem'>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col hidden">{{form.ItemRefFullName|as_crispy_field}}</div>
|
||||
<div class="col-3" onchange='descchanged(event)'>{{form.Desc|as_crispy_field}}</div>
|
||||
<div class="col-1" onchange='qtychanged(event)'>{{form.Quantity|as_crispy_field}}</div>
|
||||
<div class="col-1">{{form.UnitOfMeasure|as_crispy_field}}</div>
|
||||
<div class="col-2" onchange='ratechanged(event)'>{{form.Rate|as_crispy_field}}</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class='col text-end' onchange='amountchanged(event)'>{{form.Amount|as_crispy_field}}</div>
|
||||
<div class="col-1 mt-1" onchange='deletechanged(event)' onclick='return confirm("Are you sure DELETE this Item Line?")'>{{form.DELETE|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{form.Invoiced.as_hidden}}
|
||||
{{form.LineIsManuallyClosed.as_hidden}}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<br>
|
||||
|
||||
{% endif %}
|
||||
<div class="row justify-content-between ">
|
||||
<div class="col-5 mt-3">
|
||||
<button type="button" class="btn btn-primary me-2" id='add-more'>+ More ItemLine</button>
|
||||
<button type="submit" class="btn btn-primary me-2 ms-2" id="id_btnsave" onclick="checksubmit(event)">Save</button>
|
||||
<button type="button" class="btn btn-primary me-2 ms-2" onclick="revert()" id="id_btnrevert">Revert</button>
|
||||
{% comment %} <div class="col-4"> {% endcomment %}
|
||||
</div>
|
||||
<div class='col text-end mt-3 fs-5 fw-bold'>
|
||||
Total Amount Rp.
|
||||
</div>
|
||||
<div id='div_id_TotalAmount' class='col col-md-4 mb-1 mt-2 text-end mt-3 me-4 fs-5 fw-bold'>{{form.TotalAmount.value|intcomma}}
|
||||
|
||||
{% comment %} <input type='text' name='TotalAmount' value="{{form.Amount.value|intcomma}}" class='hidden text-end fs-5 form-control' id='id_TotalAmount' readonly> {% endcomment %}
|
||||
</div>
|
||||
{% comment %} <div class='col-1'></div> {% endcomment %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<div id='empty-form' class='hidden'>
|
||||
<div class="row parentrow">
|
||||
<div class="col-2">
|
||||
<div id='div_id_form-__prefix__-inputitem' class='mb-1'>
|
||||
{% comment %} <label for="id_form-__prefix__-inputitem" class="form-label requiredField"> ItemRefFullName<span class="asteriskField">*</span>
|
||||
</label> {% endcomment %}
|
||||
<input type='text' list='itemname' onchange='changeitem(event)' value='{{qs.ItemRefFullName}}' class='textinput form-control'
|
||||
id='id_form-__prefix__-inputitem'>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col hidden">{{formset.empty_form.ItemRefFullName|as_crispy_field}}</div>
|
||||
<div class="col-3">{{formset.empty_form.Desc|as_crispy_field}}</div>
|
||||
<div class="col-1" onchange='qtychanged(event)'>{{formset.empty_form.Quantity|as_crispy_field}}</div>
|
||||
<div class="col-1">{{formset.empty_form.UnitOfMeasure|as_crispy_field}}</div>
|
||||
<div class="col-2" onchange='ratechanged(event)'>{{formset.empty_form.Rate|as_crispy_field}}</div>
|
||||
{% comment %} <div class="col" onchange='amountchanged(event)'>{{formset.empty_form.Amount|as_crispy_field}}</div> {% endcomment %}
|
||||
{% comment %} <div class="col">{{formset.empty_form.DELETE|as_crispy_field}}</div> {% endcomment %}
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class='col text-end' onchange='amountchanged(event)'>{{formset.empty_form.Amount|as_crispy_field}}</div>
|
||||
<div class="col-1 mt-1" onchange='deletechanged(event)'>{{formset.empty_form.DELETE|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{formset.empty_form.Invoiced.as_hidden}}
|
||||
{{formset.empty_form.LineIsManuallyClosed.as_hidden}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<datalist id='itemname'>
|
||||
{% for item in itemdatalist %}
|
||||
<option value="{{item.FullName}}" data-pk="{{item.id}}" data-desc="{{item.SalesDesc}}" data-rate="{{item.SalesPrice}}">
|
||||
{% endfor %}
|
||||
</datalist>
|
||||
|
||||
|
||||
<script>
|
||||
document.getElementById('id_CustomerRefFullName').addEventListener('focusout', function() {
|
||||
console.log(this.value);
|
||||
if (this.value == '') {
|
||||
return};
|
||||
let str='';
|
||||
let url="{% url 'Invoice:getpricelist' %}"+this.value
|
||||
console.log(url)
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
//console.log(data);
|
||||
data=JSON.parse(data);
|
||||
let dt=JSON.parse(data['itemdatalist']);
|
||||
console.log(dt)
|
||||
console.log(dt.length)
|
||||
|
||||
for (let k=0;k<dt.length;k++){
|
||||
//console.log(dt[k]);
|
||||
//console.log("dt[k]")
|
||||
if (dt[k].itempricelevel__Price) {
|
||||
str+='<option value="' + dt[k].FullName + '" data-pk="' + dt[k].id + '" data-desc="' + dt[k].SalesDesc + '" data-rate="' + dt[k].itempricelevel__Price + '">';
|
||||
} else {
|
||||
str+='<option value="' + dt[k].FullName + '" data-pk="' + dt[k].id + '" data-desc="' + dt[k].SalesDesc + '" data-rate="' + dt[k].SalesPrice + '">';
|
||||
}
|
||||
}
|
||||
//console.log(str);
|
||||
dt=JSON.parse(data['obj']);
|
||||
console.log(dt)
|
||||
console.log(dt.length)
|
||||
document.getElementById('itemname').innerHTML = str;
|
||||
|
||||
str='';
|
||||
for (let k=0;k<dt.length;k++){
|
||||
console.log(dt[0].pk);
|
||||
console.log(dt[0].fields.BillAddr1);
|
||||
|
||||
str= dt[0].fields.BillAddr1;
|
||||
if (dt[0].fields.BillAddr2) {str+="\n"+dt[0].fields.BillAddr2}
|
||||
if (dt[0].fields.BillAddr3) {str+="\n"+dt[0].fields.BillAddr3}
|
||||
if (dt[0].fields.BillAddr4) {str+="\n"+dt[0].fields.BillAddr4}
|
||||
if (dt[0].fields.BillAddr5) {str+="\n"+dt[0].fields.BillAddr5}
|
||||
document.getElementById('id_BillAddr1').value = str
|
||||
str=dt[0].fields.ShipAddr1;
|
||||
if (dt[0].fields.ShipAddr2) {str+="\n"+dt[0].fields.ShipAddr2}
|
||||
if (dt[0].fields.ShipAddr3) {str+="\n"+dt[0].fields.ShipAddr3}
|
||||
if (dt[0].fields.ShipAddr4) {str+="\n"+dt[0].fields.ShipAddr4}
|
||||
if (dt[0].fields.ShipAddr5) {str+="\n"+dt[0].fields.ShipAddr5}
|
||||
document.getElementById('id_ShipAddr1').value = str;
|
||||
//document.getElementById('id_ShipAddr1').value = dt[0].fields.ShipAddr1;
|
||||
}
|
||||
|
||||
});
|
||||
console.log(str)
|
||||
//for (let i=0;i<12;i++){
|
||||
// str+= '<option value="' + i + '">';
|
||||
//}
|
||||
//document.getElementById('itemname').innerHTML = str;
|
||||
});
|
||||
function customerChanged(e){
|
||||
console.log(e)
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
function number_format( number, decimals, dec_point, thousands_sep ) {
|
||||
// http://kevin.vanzonneveld.net
|
||||
// + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
|
||||
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
||||
// + bugfix by: Michael White (http://crestidg.com)
|
||||
// + bugfix by: Benjamin Lupton
|
||||
// + bugfix by: Allan Jensen (http://www.winternet.no)
|
||||
// + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
|
||||
// * example 1: number_format(1234.5678, 2, '.', '');
|
||||
// * returns 1: 1234.57
|
||||
|
||||
var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
|
||||
var d = dec_point == undefined ? "," : dec_point;
|
||||
var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : "";
|
||||
var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
|
||||
|
||||
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var totalAmount = 0;
|
||||
var boloktosave = 0;
|
||||
|
||||
function revert(){
|
||||
document.getElementById("form").reset();
|
||||
findTotalAmount()
|
||||
}
|
||||
|
||||
function numberOnly(event){
|
||||
//console.log(event.cancelable);
|
||||
if ((event.charCode >= 48 && event.charCode <= 57) || event.charCode==46) {
|
||||
//console.log("true");
|
||||
return true;
|
||||
} else {
|
||||
//console.log("false");
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function checksubmit(e){
|
||||
console.log('boloktosave:' + boloktosave);
|
||||
if (boloktosave==0) {
|
||||
console.log("prevented")
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
const customerreffullname = document.getElementById('id_CustomerRefFullName');
|
||||
if (!customerreffullname.value){
|
||||
console.log('customer is empty');
|
||||
e.preventDefault();
|
||||
return false
|
||||
|
||||
}
|
||||
const inputitem = document.getElementsByClassName('inputitem');
|
||||
for (let i=0; i<inputitem.length;i++) {
|
||||
if (!inputitem[i].value){
|
||||
console.log(getElementByElEventClass(inputitem[i], "checkboxinput").checked)
|
||||
if (getElementByElEventClass(inputitem[i], "checkboxinput").checked==true) {
|
||||
continue;
|
||||
};
|
||||
//console.log()
|
||||
console.log("ItemRefFullName cannot be empty");
|
||||
alert('ItemRefFullName cannot be empty');
|
||||
console.log(i);
|
||||
console.log(inputitem[i]);
|
||||
inputitem[i].focus();
|
||||
e.preventDefault();
|
||||
return false
|
||||
}
|
||||
}
|
||||
console.log("submitted")
|
||||
}
|
||||
|
||||
function changeitem(e) {
|
||||
console.log(e.cancelable);
|
||||
const inputValue=e.target.value;
|
||||
console.log("inputval="+inputValue);
|
||||
const dtlist = document.getElementById('itemname');
|
||||
const selectedOption = dtlist.querySelector('option[value="' + inputValue + '"]');
|
||||
console.log(selectedOption)
|
||||
|
||||
if (!selectedOption && e.target.value!="") {
|
||||
console.log("cannot find item")
|
||||
//e.preventDefault;
|
||||
boloktosave=0;
|
||||
e.target.focus();
|
||||
e.target.onblur= function() {
|
||||
setTimeout(function() {
|
||||
e.target.focus();
|
||||
}, 0);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
if (e.target.value=="" || !e.target.value){
|
||||
boloktosave=0;
|
||||
return false;
|
||||
}
|
||||
e.target.onblur="";
|
||||
boloktosave=1;
|
||||
const datasetPK = selectedOption.dataset.pk;
|
||||
const datasetDesc = selectedOption.dataset.desc;
|
||||
const datasetRate = selectedOption.dataset.rate;
|
||||
console.log(datasetPK, datasetDesc, datasetRate);
|
||||
const targetELParent = e.target.parentElement.parentElement.parentElement;
|
||||
console.log(targetELParent);
|
||||
//const targetELParent = e.target.parentElement.parentElement;
|
||||
//const targetELItemRef = targetELParent.nextElementSibling.children[0].children[1];
|
||||
const targetELItemRef = targetELParent.getElementsByClassName('itemreffullname')[0];
|
||||
console.log(targetELItemRef);
|
||||
targetELItemRef.value = datasetPK;
|
||||
//const targetELDesc = targetELParent.nextElementSibling.nextElementSibling.children[0].children[1];
|
||||
const targetELDesc = getElementByElEventClass(e, "desc");
|
||||
//const targetELDesc = targetELParent.getElementsByClassName('desc')[0];
|
||||
console.log(targetELDesc);
|
||||
targetELDesc.value = datasetDesc;
|
||||
//const targetELRate = targetELParent.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.children[0].children[1];
|
||||
const targetELRate = getElementByElEventClass(e, "rate");
|
||||
//const targetELRate = targetELParent.getElementsByClassName('rate')[0];
|
||||
console.log(targetELRate);
|
||||
targetELRate.value = parseFloat(datasetRate).toFixed(2);
|
||||
//const targetELAmount = targetELParent.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.children[0].children[1];
|
||||
const targetELAmount = getElementByElEventClass(e, "amount");
|
||||
//const targetELAmount = targetELParent.getElementsByClassName('amount')[0];
|
||||
//const qty = targetELParent.nextElementSibling.nextElementSibling.nextElementSibling.children[0].children[1].value;
|
||||
const qtyEL = getElementByElEventClass(e, "quantity");
|
||||
//const qtyEL = targetELParent.getElementsByClassName('quantity')[0];
|
||||
console.log(qtyEL.value);
|
||||
if (!qtyEL.value){
|
||||
qtyEL.value=1;
|
||||
}
|
||||
targetELAmount.value=(qtyEL.value*parseFloat(datasetRate)).toFixed(2)
|
||||
findTotalAmount();
|
||||
}
|
||||
|
||||
function findTotalAmount() {
|
||||
let arr=document.getElementsByClassName('amount');
|
||||
let tot=0;
|
||||
for(let i=0;i<arr.length;i++){
|
||||
if(parseFloat(arr[i].value)) {
|
||||
tot += parseFloat(arr[i].value);
|
||||
}
|
||||
}
|
||||
console.log(tot);
|
||||
const totalamountEL=document.getElementById('id_TotalAmount');
|
||||
const dividtotalamountEL = document.getElementById('div_id_TotalAmount');
|
||||
const totFormated = number_format(tot,"2",".",",");
|
||||
console.log(totFormated);
|
||||
console.log(dividtotalamountEL)
|
||||
dividtotalamountEL.innerHTML = totFormated;
|
||||
console.log(dividtotalamountEL.innerHTML)
|
||||
totalamountEL.value=tot;
|
||||
}
|
||||
|
||||
function descchanged(e) {
|
||||
boloktosave=1;
|
||||
}
|
||||
function qtychanged(e) {
|
||||
console.log(e);
|
||||
let qty=e.target.value;
|
||||
console.log(qty);
|
||||
if (!qty) {
|
||||
qty=1;
|
||||
e.target.value=qty;
|
||||
} else if (qty<0) {
|
||||
qty=0;
|
||||
e.target.value=qty;
|
||||
}
|
||||
const rate=getElementByElEventClass(e, "rate").value;
|
||||
|
||||
//const rate=e.target.parentElement.parentElement.nextElementSibling.nextElementSibling.children[0].children[1].value;
|
||||
console.log(rate * qty);
|
||||
let amountValue=rate*qty;
|
||||
const amount=getElementByElEventClass(e, "amount")
|
||||
//let amount=e.target.parentElement.parentElement.nextElementSibling.nextElementSibling.nextElementSibling.children[0].children[1];
|
||||
console.log(amount);
|
||||
amount.value=amountValue.toFixed(2);
|
||||
findTotalAmount();
|
||||
boloktosave=1;
|
||||
}
|
||||
|
||||
function ratechanged(e) {
|
||||
console.log(e);
|
||||
let rate=e.target.value;
|
||||
console.log(rate);
|
||||
if (!rate){
|
||||
rate=0;
|
||||
e.target.value=0;
|
||||
} else if(rate<0){
|
||||
rate=0;
|
||||
e.target.value=0;
|
||||
}
|
||||
const qty=getElementByElEventClass(e, "quantity").value;
|
||||
//const qty=e.target.parentElement.parentElement.previousElementSibling.previousElementSibling.children[0].children[1].value;
|
||||
console.log("qty"+qty);
|
||||
let amountValue=rate*qty;
|
||||
console.log(amountValue);
|
||||
const amount=getElementByElEventClass(e,"amount");
|
||||
//const amount=e.target.parentElement.parentElement.nextElementSibling.children[0].children[1];
|
||||
console.log(amount);
|
||||
amount.value=amountValue.toFixed(2);
|
||||
console.log(amount.value);
|
||||
findTotalAmount();
|
||||
boloktosave=1;
|
||||
}
|
||||
|
||||
function amountchanged(e) {
|
||||
console.log(e);
|
||||
let amount=e.target.value;
|
||||
if (!amount){
|
||||
amount=0;
|
||||
e.target.value=0;
|
||||
} else if(amount<0){
|
||||
amount=0;
|
||||
e.target.value=0;
|
||||
alert("Amount cannot be a negative value.")
|
||||
}
|
||||
const qty=getElementByElEventClass(e, "quantity");
|
||||
console.log(qty.value);
|
||||
if (qty.value){
|
||||
const rateEL=getElementByElEventClass(e, "rate")
|
||||
if (qty.value>0){
|
||||
rateEL.value=(amount/qty.value).toFixed(2)
|
||||
|
||||
} else {
|
||||
qty.value=(amount/rateEL.value).toFixed(2)
|
||||
}
|
||||
findTotalAmount();
|
||||
boloktosave=1;
|
||||
}
|
||||
}
|
||||
|
||||
function getElementByElEventClass(e, classname){
|
||||
let parent;
|
||||
console.log("eventclaaaaaaasss");
|
||||
console.log("eventclass",e,classname)
|
||||
if (e.target){
|
||||
parent = e.target.parentElement;
|
||||
}
|
||||
else {
|
||||
parent = e.parentElement;
|
||||
}
|
||||
console.log(parent);
|
||||
console.log(e, classname);
|
||||
while (!parent.classList.contains('parentrow')){
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
console.log(parent);
|
||||
//const parent=e.target.parentElement.parentElement.parentElement;
|
||||
console.log(parent);
|
||||
const el=parent.getElementsByClassName(classname)[0];
|
||||
console.log(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
function deletechanged(e){
|
||||
console.log("DELETEEEEE")
|
||||
boloktosave=1;
|
||||
findTotalAmount();
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener('click', (event)=>{
|
||||
if (event.target.id == 'add-more') {
|
||||
console.log("addmore clicked")
|
||||
add_new_form(event);
|
||||
}
|
||||
})
|
||||
function add_new_form(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
const totalNewForms = document.getElementById('id_invoiceitemline_set-TOTAL_FORMS'); //'id_invoiceitemline_set-TOTAL_FORMS' 'id_form-TOTAL_FORMS'
|
||||
const curItemLineForms = document.getElementsByClassName('itemline-form');
|
||||
const curFormCount = curItemLineForms.length;
|
||||
console.log('curformcount=' + curFormCount);
|
||||
const formCopyTarget = document.getElementById('itemline-form-list');
|
||||
const copyEmptyFormEl = document.getElementById('empty-form').cloneNode(true);
|
||||
copyEmptyFormEl.setAttribute('class', 'itemline-form');
|
||||
copyEmptyFormEl.setAttribute('id', `form-${curFormCount}`);
|
||||
const regex = new RegExp('__prefix__', 'g');
|
||||
copyEmptyFormEl.innerHTML = copyEmptyFormEl.innerHTML.replace(regex, curFormCount);
|
||||
totalNewForms.setAttribute('value', curFormCount + 1);
|
||||
formCopyTarget.append(copyEmptyFormEl);
|
||||
|
||||
}
|
||||
</script>
|
||||
{% endblock body %}
|
||||
8
django/Invoice/templates/Invoice/home.html
Normal file
8
django/Invoice/templates/Invoice/home.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends 'base.html'%}
|
||||
{% block body %}
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
{% endblock %}
|
||||
1
django/Invoice/templates/Invoice/index_invoice.html
Normal file
1
django/Invoice/templates/Invoice/index_invoice.html
Normal file
@ -0,0 +1 @@
|
||||
{% include 'Item/table_index.html' with title="Invoice Order Index" %}
|
||||
406
django/Invoice/templates/Invoice/show-customers.html
Normal file
406
django/Invoice/templates/Invoice/show-customers.html
Normal file
@ -0,0 +1,406 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load humanize %}
|
||||
{% block title %}Invoice{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
{% comment %} {% if messages %}
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{message}}
|
||||
</div>
|
||||
{% endif %} {% endcomment %}
|
||||
{% if messages %}
|
||||
<ul class="messages">
|
||||
{% for message in messages %}
|
||||
<li{% if message.tags %} class="alert alert-{{ message.tags }}"{% endif %}>{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<div class="container-fluid">
|
||||
<form action="" method="POST" class="card border-info-subtle p-3 mt-2" autocomplete="off" id="form">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
</div>
|
||||
<!-- <input name="customer_fullname" type='text' list='itemname' onchange='changeitem(event)' value='{{customer_name}}' class='textinput form-control'> -->
|
||||
<input name="customerreffullname" type='text' list='itemname' value='{{customer_name}}' id='id_CustomerRefFullName' class='textinput form-control' autofocus>
|
||||
|
||||
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" id="id_btnsave" onclick="checksubmit(event)">Save</button>
|
||||
<!-- <button type="submit" class="btn btn-primary mt-2 me-2 ms-2" id="id_btnsave" >Save</button> -->
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<datalist id='itemname'>
|
||||
{% for customer in customers %}
|
||||
<option value="{{customer|first}}" data-pk="{{customer|first}}" data-desc="{{customer|first}}" data-rate="{{customer|first}}">
|
||||
{% endfor %}
|
||||
</datalist>
|
||||
|
||||
<!--
|
||||
<script>
|
||||
document.getElementById('id_CustomerRefFullName').addEventListener('focusout', function() {
|
||||
console.log(this.value);
|
||||
if (this.value == '') {
|
||||
return};
|
||||
let str='';
|
||||
let url="{% url 'Invoice:getpricelist' %}"+this.value
|
||||
console.log(url)
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
//console.log(data);
|
||||
data=JSON.parse(data);
|
||||
let dt=JSON.parse(data['itemdatalist']);
|
||||
console.log(dt)
|
||||
console.log(dt.length)
|
||||
|
||||
for (let k=0;k<dt.length;k++){
|
||||
//console.log(dt[k]);
|
||||
//console.log("dt[k]")
|
||||
if (dt[k].itempricelevel__Price) {
|
||||
str+='<option value="' + dt[k].FullName + '" data-pk="' + dt[k].id + '" data-desc="' + dt[k].SalesDesc + '" data-rate="' + dt[k].itempricelevel__Price + '">';
|
||||
} else {
|
||||
str+='<option value="' + dt[k].FullName + '" data-pk="' + dt[k].id + '" data-desc="' + dt[k].SalesDesc + '" data-rate="' + dt[k].SalesPrice + '">';
|
||||
}
|
||||
}
|
||||
//console.log(str);
|
||||
dt=JSON.parse(data['obj']);
|
||||
console.log(dt)
|
||||
console.log(dt.length)
|
||||
document.getElementById('itemname').innerHTML = str;
|
||||
|
||||
str='';
|
||||
for (let k=0;k<dt.length;k++){
|
||||
console.log(dt[0].pk);
|
||||
console.log(dt[0].fields.BillAddr1);
|
||||
|
||||
str= dt[0].fields.BillAddr1;
|
||||
if (dt[0].fields.BillAddr2) {str+="\n"+dt[0].fields.BillAddr2}
|
||||
if (dt[0].fields.BillAddr3) {str+="\n"+dt[0].fields.BillAddr3}
|
||||
if (dt[0].fields.BillAddr4) {str+="\n"+dt[0].fields.BillAddr4}
|
||||
if (dt[0].fields.BillAddr5) {str+="\n"+dt[0].fields.BillAddr5}
|
||||
document.getElementById('id_BillAddr1').value = str
|
||||
str=dt[0].fields.ShipAddr1;
|
||||
if (dt[0].fields.ShipAddr2) {str+="\n"+dt[0].fields.ShipAddr2}
|
||||
if (dt[0].fields.ShipAddr3) {str+="\n"+dt[0].fields.ShipAddr3}
|
||||
if (dt[0].fields.ShipAddr4) {str+="\n"+dt[0].fields.ShipAddr4}
|
||||
if (dt[0].fields.ShipAddr5) {str+="\n"+dt[0].fields.ShipAddr5}
|
||||
document.getElementById('id_ShipAddr1').value = str;
|
||||
//document.getElementById('id_ShipAddr1').value = dt[0].fields.ShipAddr1;
|
||||
}
|
||||
|
||||
});
|
||||
console.log(str)
|
||||
//for (let i=0;i<12;i++){
|
||||
// str+= '<option value="' + i + '">';
|
||||
//}
|
||||
//document.getElementById('itemname').innerHTML = str;
|
||||
});
|
||||
function customerChanged(e){
|
||||
console.log(e)
|
||||
}
|
||||
</script> -->
|
||||
<script>
|
||||
function number_format( number, decimals, dec_point, thousands_sep ) {
|
||||
// http://kevin.vanzonneveld.net
|
||||
// + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
|
||||
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
||||
// + bugfix by: Michael White (http://crestidg.com)
|
||||
// + bugfix by: Benjamin Lupton
|
||||
// + bugfix by: Allan Jensen (http://www.winternet.no)
|
||||
// + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
|
||||
// * example 1: number_format(1234.5678, 2, '.', '');
|
||||
// * returns 1: 1234.57
|
||||
|
||||
var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
|
||||
var d = dec_point == undefined ? "," : dec_point;
|
||||
var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : "";
|
||||
var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
|
||||
|
||||
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var totalAmount = 0;
|
||||
var boloktosave = 0;
|
||||
|
||||
function revert(){
|
||||
document.getElementById("form").reset();
|
||||
findTotalAmount()
|
||||
}
|
||||
|
||||
function numberOnly(event){
|
||||
//console.log(event.cancelable);
|
||||
if ((event.charCode >= 48 && event.charCode <= 57) || event.charCode==46) {
|
||||
//console.log("true");
|
||||
return true;
|
||||
} else {
|
||||
//console.log("false");
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function checksubmit(e){
|
||||
console.log('boloktosave:' + boloktosave);
|
||||
// if (boloktosave==0) {
|
||||
// console.log("prevented")
|
||||
// e.preventDefault();
|
||||
// return false;
|
||||
// }
|
||||
const customerreffullname = document.getElementById('id_CustomerRefFullName');
|
||||
if (!customerreffullname.value){
|
||||
console.log('customer is empty');
|
||||
e.preventDefault();
|
||||
return false
|
||||
|
||||
}
|
||||
const inputitem = document.getElementsByClassName('inputitem');
|
||||
const inputValue=document.getElementById('id_CustomerRefFullName').value;
|
||||
const dtlist = document.getElementById('itemname');
|
||||
console.log(inputValue)
|
||||
const selectedOption = dtlist.querySelector('option[value="' + inputValue + '"]');
|
||||
console.log("selected option:")
|
||||
console.log(selectedOption);
|
||||
if (!selectedOption) {
|
||||
console.log("not correct custname")
|
||||
e.preventDefault();
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// for (let i=0; i<inputitem.length;i++) {
|
||||
// if (!inputitem[i].value){
|
||||
// console.log(getElementByElEventClass(inputitem[i], "checkboxinput").checked)
|
||||
// if (getElementByElEventClass(inputitem[i], "checkboxinput").checked==true) {
|
||||
// continue;
|
||||
// };
|
||||
// //console.log()
|
||||
// console.log("ItemRefFullName cannot be empty");
|
||||
// alert('ItemRefFullName cannot be empty');
|
||||
// console.log(i);
|
||||
// console.log(inputitem[i]);
|
||||
// inputitem[i].focus();
|
||||
// e.preventDefault();
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
console.log("submitted")
|
||||
}
|
||||
|
||||
function changeitem(e) {
|
||||
console.log(e.cancelable);
|
||||
const inputValue=e.target.value;
|
||||
console.log("inputval="+inputValue);
|
||||
const dtlist = document.getElementById('itemname');
|
||||
const selectedOption = dtlist.querySelector('option[value="' + inputValue + '"]');
|
||||
console.log(selectedOption)
|
||||
|
||||
if (!selectedOption && e.target.value!="") {
|
||||
console.log("cannot find item")
|
||||
//e.preventDefault;
|
||||
boloktosave=0;
|
||||
e.target.focus();
|
||||
e.target.onblur= function() {
|
||||
setTimeout(function() {
|
||||
e.target.focus();
|
||||
}, 0);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
if (e.target.value=="" || !e.target.value){
|
||||
boloktosave=0;
|
||||
return false;
|
||||
}
|
||||
e.target.onblur="";
|
||||
boloktosave=1;
|
||||
const datasetPK = selectedOption.dataset.pk;
|
||||
const datasetDesc = selectedOption.dataset.desc;
|
||||
const datasetRate = selectedOption.dataset.rate;
|
||||
console.log(datasetPK, datasetDesc, datasetRate);
|
||||
const targetELParent = e.target.parentElement.parentElement.parentElement;
|
||||
console.log(targetELParent);
|
||||
//const targetELParent = e.target.parentElement.parentElement;
|
||||
//const targetELItemRef = targetELParent.nextElementSibling.children[0].children[1];
|
||||
const targetELItemRef = targetELParent.getElementsByClassName('itemreffullname')[0];
|
||||
console.log(targetELItemRef);
|
||||
targetELItemRef.value = datasetPK;
|
||||
//const targetELDesc = targetELParent.nextElementSibling.nextElementSibling.children[0].children[1];
|
||||
const targetELDesc = getElementByElEventClass(e, "desc");
|
||||
//const targetELDesc = targetELParent.getElementsByClassName('desc')[0];
|
||||
console.log(targetELDesc);
|
||||
targetELDesc.value = datasetDesc;
|
||||
//const targetELRate = targetELParent.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.children[0].children[1];
|
||||
const targetELRate = getElementByElEventClass(e, "rate");
|
||||
//const targetELRate = targetELParent.getElementsByClassName('rate')[0];
|
||||
console.log(targetELRate);
|
||||
targetELRate.value = parseFloat(datasetRate).toFixed(2);
|
||||
//const targetELAmount = targetELParent.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.children[0].children[1];
|
||||
const targetELAmount = getElementByElEventClass(e, "amount");
|
||||
//const targetELAmount = targetELParent.getElementsByClassName('amount')[0];
|
||||
//const qty = targetELParent.nextElementSibling.nextElementSibling.nextElementSibling.children[0].children[1].value;
|
||||
const qtyEL = getElementByElEventClass(e, "quantity");
|
||||
//const qtyEL = targetELParent.getElementsByClassName('quantity')[0];
|
||||
console.log(qtyEL.value);
|
||||
if (!qtyEL.value){
|
||||
qtyEL.value=1;
|
||||
}
|
||||
targetELAmount.value=(qtyEL.value*parseFloat(datasetRate)).toFixed(2)
|
||||
findTotalAmount();
|
||||
}
|
||||
|
||||
function findTotalAmount() {
|
||||
let arr=document.getElementsByClassName('amount');
|
||||
let tot=0;
|
||||
for(let i=0;i<arr.length;i++){
|
||||
if(parseFloat(arr[i].value)) {
|
||||
tot += parseFloat(arr[i].value);
|
||||
}
|
||||
}
|
||||
console.log(tot);
|
||||
const totalamountEL=document.getElementById('id_TotalAmount');
|
||||
const dividtotalamountEL = document.getElementById('div_id_TotalAmount');
|
||||
const totFormated = number_format(tot,"2",".",",");
|
||||
console.log(totFormated);
|
||||
console.log(dividtotalamountEL)
|
||||
dividtotalamountEL.innerHTML = totFormated;
|
||||
console.log(dividtotalamountEL.innerHTML)
|
||||
totalamountEL.value=tot;
|
||||
}
|
||||
|
||||
function descchanged(e) {
|
||||
boloktosave=1;
|
||||
}
|
||||
function qtychanged(e) {
|
||||
console.log(e);
|
||||
let qty=e.target.value;
|
||||
console.log(qty);
|
||||
if (!qty) {
|
||||
qty=1;
|
||||
e.target.value=qty;
|
||||
} else if (qty<0) {
|
||||
qty=0;
|
||||
e.target.value=qty;
|
||||
}
|
||||
const rate=getElementByElEventClass(e, "rate").value;
|
||||
|
||||
//const rate=e.target.parentElement.parentElement.nextElementSibling.nextElementSibling.children[0].children[1].value;
|
||||
console.log(rate * qty);
|
||||
let amountValue=rate*qty;
|
||||
const amount=getElementByElEventClass(e, "amount")
|
||||
//let amount=e.target.parentElement.parentElement.nextElementSibling.nextElementSibling.nextElementSibling.children[0].children[1];
|
||||
console.log(amount);
|
||||
amount.value=amountValue.toFixed(2);
|
||||
findTotalAmount();
|
||||
boloktosave=1;
|
||||
}
|
||||
|
||||
function ratechanged(e) {
|
||||
console.log(e);
|
||||
let rate=e.target.value;
|
||||
console.log(rate);
|
||||
if (!rate){
|
||||
rate=0;
|
||||
e.target.value=0;
|
||||
} else if(rate<0){
|
||||
rate=0;
|
||||
e.target.value=0;
|
||||
}
|
||||
const qty=getElementByElEventClass(e, "quantity").value;
|
||||
//const qty=e.target.parentElement.parentElement.previousElementSibling.previousElementSibling.children[0].children[1].value;
|
||||
console.log("qty"+qty);
|
||||
let amountValue=rate*qty;
|
||||
console.log(amountValue);
|
||||
const amount=getElementByElEventClass(e,"amount");
|
||||
//const amount=e.target.parentElement.parentElement.nextElementSibling.children[0].children[1];
|
||||
console.log(amount);
|
||||
amount.value=amountValue.toFixed(2);
|
||||
console.log(amount.value);
|
||||
findTotalAmount();
|
||||
boloktosave=1;
|
||||
}
|
||||
|
||||
function amountchanged(e) {
|
||||
console.log(e);
|
||||
let amount=e.target.value;
|
||||
if (!amount){
|
||||
amount=0;
|
||||
e.target.value=0;
|
||||
} else if(amount<0){
|
||||
amount=0;
|
||||
e.target.value=0;
|
||||
alert("Amount cannot be a negative value.")
|
||||
}
|
||||
const qty=getElementByElEventClass(e, "quantity");
|
||||
console.log(qty.value);
|
||||
if (qty.value){
|
||||
const rateEL=getElementByElEventClass(e, "rate")
|
||||
if (qty.value>0){
|
||||
rateEL.value=(amount/qty.value).toFixed(2)
|
||||
|
||||
} else {
|
||||
qty.value=(amount/rateEL.value).toFixed(2)
|
||||
}
|
||||
findTotalAmount();
|
||||
boloktosave=1;
|
||||
}
|
||||
}
|
||||
|
||||
function getElementByElEventClass(e, classname){
|
||||
let parent;
|
||||
console.log("eventclaaaaaaasss");
|
||||
console.log("eventclass",e,classname)
|
||||
if (e.target){
|
||||
parent = e.target.parentElement;
|
||||
}
|
||||
else {
|
||||
parent = e.parentElement;
|
||||
}
|
||||
console.log(parent);
|
||||
console.log(e, classname);
|
||||
while (!parent.classList.contains('parentrow')){
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
console.log(parent);
|
||||
//const parent=e.target.parentElement.parentElement.parentElement;
|
||||
console.log(parent);
|
||||
const el=parent.getElementsByClassName(classname)[0];
|
||||
console.log(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
function deletechanged(e){
|
||||
console.log("DELETEEEEE")
|
||||
boloktosave=1;
|
||||
findTotalAmount();
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener('click', (event)=>{
|
||||
if (event.target.id == 'add-more') {
|
||||
console.log("addmore clicked")
|
||||
add_new_form(event);
|
||||
}
|
||||
})
|
||||
function add_new_form(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
const totalNewForms = document.getElementById('id_invoiceitemline_set-TOTAL_FORMS'); //'id_invoiceitemline_set-TOTAL_FORMS' 'id_form-TOTAL_FORMS'
|
||||
const curItemLineForms = document.getElementsByClassName('itemline-form');
|
||||
const curFormCount = curItemLineForms.length;
|
||||
console.log('curformcount=' + curFormCount);
|
||||
const formCopyTarget = document.getElementById('itemline-form-list');
|
||||
const copyEmptyFormEl = document.getElementById('empty-form').cloneNode(true);
|
||||
copyEmptyFormEl.setAttribute('class', 'itemline-form');
|
||||
copyEmptyFormEl.setAttribute('id', `form-${curFormCount}`);
|
||||
const regex = new RegExp('__prefix__', 'g');
|
||||
copyEmptyFormEl.innerHTML = copyEmptyFormEl.innerHTML.replace(regex, curFormCount);
|
||||
totalNewForms.setAttribute('value', curFormCount + 1);
|
||||
formCopyTarget.append(copyEmptyFormEl);
|
||||
|
||||
}
|
||||
</script>
|
||||
{% endblock body %}
|
||||
47
django/Invoice/templates/Invoice/so_list_form.html
Normal file
47
django/Invoice/templates/Invoice/so_list_form.html
Normal file
@ -0,0 +1,47 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block body %}
|
||||
<!-- <form method="POST" action="{{ request.path }}"> -->
|
||||
<div class="container">
|
||||
<form method="POST" action="{% url 'Invoice:show_inv' %}" class="modal-content">
|
||||
{% csrf_token %}
|
||||
<div class="modal-header">
|
||||
<h1>Choose SO</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3 mt-3">
|
||||
<input type="hidden" name="customer_fullname" value="{{ customer_fullname }}">
|
||||
<!-- <input name="customerreffullname" type='text' list='itemname' value='{{customer_name}}' id='id_CustomerRefFullName' class='textinput form-control' autofocus> -->
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Amount</th>
|
||||
<th scope="col">SO Number</th>
|
||||
<!-- <th scope="col">Handle</th> -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for so_no in objects %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" name="so_field" value="{{ so_no.0 }}" id="id_so_field_{{forloop.counter}}" class="form-check-input">
|
||||
<label for="id_so_field_{{ forloop.counter }}" class="form-check-label">{{ so_no.1 }}
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ so_no.2 }}</td>
|
||||
<td>{{ so_no.3 }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary mt-2 me-2 ms-2" id="id_btnsave" >Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
105
django/Invoice/templates/Invoice/temp.html
Normal file
105
django/Invoice/templates/Invoice/temp.html
Normal file
@ -0,0 +1,105 @@
|
||||
{% comment %} {{form.CustomerRefFullName.errors}}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<ul>
|
||||
{% for error in form.non_field_errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% for hidden_field in form.hidden_fields %}
|
||||
{% if hidden_field.errors %}
|
||||
<ul>
|
||||
{% for error in hidden_field.errors %}
|
||||
<li>(Hidden field {{ hidden_field.name }}) {{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{{ hidden_field }}
|
||||
{% endfor %}
|
||||
|
||||
<table border="1">
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
<th>{{ field.label_tag }}</th>
|
||||
<td>
|
||||
{% if field.errors %}
|
||||
<ul>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<br />{{ field.help_text }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table> {% endcomment %}
|
||||
|
||||
|
||||
|
||||
{% comment %} <div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-9">
|
||||
<div class="row">
|
||||
<div class="col-9">
|
||||
I'm the first element in the nested grid
|
||||
</div>
|
||||
<div class="col-3">
|
||||
I'm the second element in the nested grid
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
I'm the second element in the first grid
|
||||
</div>
|
||||
<div class="col-9">
|
||||
I'm the third element in the first grid
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">{{form.CustomerRefFullName|as_crispy_field}}</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">{{form.BillAddr1|as_crispy_field}}</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col">{{form.TxnDate|as_crispy_field}}</div>
|
||||
<div class="col">{{form.RefNumber|as_crispy_field}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-5l">{{form.CustomerRefFullName|as_crispy_field}}</div>
|
||||
<div class="col-4">{{form.BillAddr3|as_crispy_field}}</div>
|
||||
<div class="col">{{form.RefNumber|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">Outer row 1, column 3 - spans two rows (column 2 has two rows)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">{{form.BillAddr1|as_crispy_field}}</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col">{{form.CustomerRefFullName|as_crispy_field}}</div>
|
||||
<div class="col">{{form.CustomerRefFullName|as_crispy_field}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">{{form.CustomerRefFullName|as_crispy_field}}</div>
|
||||
<div class="col">{{form.CustomerRefFullName|as_crispy_field}}</div>
|
||||
<div class="col">{{form.CustomerRefFullName|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">Outer row 1, column 3 - spans two rows (column 2 has two rows)</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
3
django/Invoice/tests.py
Normal file
3
django/Invoice/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
19
django/Invoice/urls.py
Normal file
19
django/Invoice/urls.py
Normal file
@ -0,0 +1,19 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = "Invoice"
|
||||
|
||||
urlpatterns = [
|
||||
# path('', views.home_view ),
|
||||
path('', views.show_customer, name="show_customer" ),
|
||||
path('selectso', views.select_so, name="select_so" ),
|
||||
path('showinv', views.show_inv, name="show_inv" ),
|
||||
|
||||
|
||||
path('', views.index, name="index"),
|
||||
path('add', views.add_so, name="add_so"),
|
||||
path('edit/<int:pk>', views.add_so, name="edit_so"),
|
||||
path('delete/<int:pk>', views.delete_so, name="delete_so"),
|
||||
path('getpricelist/<int:pk>', views.getpricelist_so, name="getpricelist_so"),
|
||||
path('getpricelist/', views.getpricelist_so, name="getpricelist"),
|
||||
]
|
||||
300
django/Invoice/views.py
Normal file
300
django/Invoice/views.py
Normal file
@ -0,0 +1,300 @@
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.forms import modelformset_factory, inlineformset_factory
|
||||
from django.db import transaction
|
||||
from django.core import serializers
|
||||
from .models import Invoice, InvoiceItemLine
|
||||
from Item.models import Item, PriceLevel, PriceLevelItem
|
||||
from Customer.models import Customer
|
||||
from .forms import InvoiceForm, InvoiceItemLineForm, GeeksForm
|
||||
import json
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.contrib import messages
|
||||
import os
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def show_customer(request):
|
||||
pass
|
||||
|
||||
thispath = os.getcwd()
|
||||
print(thispath)
|
||||
parent = os.path.dirname(thispath)
|
||||
print(parent)
|
||||
customers_file = os.path.join(parent, "ItemInventory", "CustomerList.xlsx")
|
||||
df = pd.read_excel(customers_file, usecols=["FullName"])
|
||||
|
||||
context={"customers": df.values.tolist()}
|
||||
if request.method =="POST":
|
||||
import sys
|
||||
sys.path.append('..')
|
||||
from SO_to_Inv.readSO import SalesOrderQuery
|
||||
print(os.getcwd())
|
||||
parentdir = os.path.dirname(os.getcwd())
|
||||
print(parentdir)
|
||||
|
||||
|
||||
customer_name = request.POST.get("customerreffullname")
|
||||
print(f'customer_name: {customer_name}')
|
||||
context['objects'] = ['abc', 'def', 'ghi', 'jkl']
|
||||
if customer_name:
|
||||
ini=SalesOrderQuery(FullName= customer_name, IncludeRetElement = ['TxnID', 'TimeCreated', 'TimeModified','TxnNumber', 'CustomerRef', 'TxnDate', 'RefNumber', 'IsManuallyClosed', 'IsFullyInvoiced','TotalAmount'], cwd=parentdir)
|
||||
print("after ini")
|
||||
# qbxml = ini.create_QBXML()
|
||||
# print(qbxml)
|
||||
response_string = ini.connect_to_quickbooks(ini.create_QBXML())
|
||||
print(response_string)
|
||||
open_sales_orders = ini.get_open_so()
|
||||
print(f'open sales orders:{open_sales_orders}')
|
||||
ini=None
|
||||
if open_sales_orders:
|
||||
context['objects'] = open_sales_orders
|
||||
|
||||
|
||||
context['customer_fullname'] = customer_name
|
||||
return render(request, "Invoice/so_list_form.html", context)
|
||||
# print(df.values.tolist())
|
||||
|
||||
|
||||
|
||||
return render(request, "Invoice/show-customers.html", context)
|
||||
|
||||
|
||||
def select_so(request):
|
||||
pass
|
||||
|
||||
def show_inv(request):
|
||||
print("show_inv")
|
||||
context={}
|
||||
if request.method == "POST":
|
||||
print(request.POST)
|
||||
if ('so_field' in request.POST) and ('customer_fullname' in request.POST):
|
||||
# print(request.POST.getlist('so_field'))
|
||||
multivals = request.POST.getlist('so_field')
|
||||
customer_fullname = request.POST.get('customer_fullname')
|
||||
print(f'Customer_fullname:{customer_fullname} -> request values:{multivals}')
|
||||
return render( request, "Invoice/so_list_form.html", context)
|
||||
# return HttpResponse('')
|
||||
|
||||
def index(request):
|
||||
print("index Inv")
|
||||
context = {}
|
||||
context['objects'] = Invoice.objects.order_by('-TimeCreated')
|
||||
context['addurl'] = reverse('Invoice:add_so')
|
||||
return render(request, "Invoice/index_invoice.html", context=context)
|
||||
DEMO_CHOICES2 =(
|
||||
("10", "Naveen"),
|
||||
("20", "Pranav"),
|
||||
("30", "Isha"),
|
||||
("40", "Saloni"),
|
||||
("50", "Sg"),
|
||||
)
|
||||
def home_view(request):
|
||||
if request.method == "POST":
|
||||
print(request.POST)
|
||||
if 'geeks_field' in request.POST:
|
||||
print(request.POST.getlist('geeks_field'))
|
||||
multivals = request.POST.getlist('geeks_field')
|
||||
print(multivals)
|
||||
form = GeeksForm(request.POST)
|
||||
if form.is_valid():
|
||||
print('good')
|
||||
demochc = ()
|
||||
for i in range(2,6):
|
||||
demochc += ((i, str(i)))
|
||||
context = {}
|
||||
print (demochc)
|
||||
form = GeeksForm()
|
||||
# form.fields['geeks_field'].choices = demochc
|
||||
context['form'] = form
|
||||
return render( request, "Invoice/home.html", context)
|
||||
|
||||
def add_so(request, pk=None):
|
||||
print("add INV")
|
||||
context = {}
|
||||
obj=None
|
||||
plname = None
|
||||
if pk:
|
||||
obj = get_object_or_404(Invoice.objects.select_related(), pk=pk)
|
||||
# obj = get_object_or_404(Invoice, pk=pk)
|
||||
plname = obj.CustomerRefFullName.PriceLevelRefFullName.Name
|
||||
# print(f"{obj.CustomerRefFullName.PriceLevelRefFullName.Name}")
|
||||
form = InvoiceForm(request.POST or None, instance=obj)
|
||||
# InvoiceItemLineFormset = modelformset_factory(InvoiceItemLine, form=InvoiceItemLineForm, extra=0) #cannot use extra >0 because the formset is zip with the qs below
|
||||
InvoiceItemLineFormset = inlineformset_factory(Invoice, InvoiceItemLine, form=InvoiceItemLineForm, extra=0) #cannot use extra >0 because the formset is zip with the qs below
|
||||
# InvoiceItemLineFormset = modelformset_factory(InvoiceItemLine, fields = ('ItemRefFullName', 'Desc', 'Quantity', 'UnitOfMeasure', 'Rate', 'Amount', 'Invoiced', 'LineIsManuallyClosed',), extra=1)
|
||||
# InvoiceItemLineFormset = modelformset_factory(InvoiceItemLine, fields = ('ItemRefFullName', 'Desc', 'Quantity', 'UnitOfMeasure', 'Rate', 'Amount', 'Invoiced', 'LineIsManuallyClosed',), extra=1)
|
||||
qs=None
|
||||
formset = InvoiceItemLineFormset(request.POST or None)
|
||||
if pk:
|
||||
print("got ID")
|
||||
qs = obj.invoiceitemline_set.all().order_by('id').select_related('ItemRefFullName')
|
||||
# formset = InvoiceItemLineFormset(request.POST or None, queryset=qs)
|
||||
formset = InvoiceItemLineFormset(request.POST or None, instance=obj)
|
||||
print(request.POST)
|
||||
# print(formset)
|
||||
# print(len(formset))
|
||||
# print(f"queryset={qs}")
|
||||
# print(f"queryset={qs[0].ItemRefFullName}")
|
||||
# qsItemList = list(x.ItemRefFullName for x in qs)
|
||||
# print(qsItemList)
|
||||
print(f'plname:{plname}')
|
||||
qsIn = Item.objects.filter(itempricelevel__PL__Name=plname).prefetch_related("itempricelevel").values('id', 'FullName','SalesDesc', 'SalesPrice', 'itempricelevel__Price')
|
||||
qsEx = Item.objects.exclude(itempricelevel__PL__Name=plname).prefetch_related("itempricelevel").values( "id", "FullName", "SalesDesc", "SalesPrice", "AlwaysNull")
|
||||
# print(qsIn)
|
||||
# print("qsEx")
|
||||
# print(qsEx)
|
||||
context['itemdatalist'] = qsIn.union(qsEx)
|
||||
|
||||
context['object'] = obj
|
||||
context['qsformset'] = None
|
||||
if pk:
|
||||
context['qsformset'] = list(zip(qs, formset))
|
||||
if request.method == "POST":
|
||||
with transaction.atomic():
|
||||
if form.is_valid():
|
||||
# print(form.cleaned_data)
|
||||
# with transaction.atomic():
|
||||
# print(formset)
|
||||
if formset.is_valid():
|
||||
for delform in formset.deleted_forms:
|
||||
pass
|
||||
totalamount=form.cleaned_data['TotalAmount']
|
||||
print(totalamount, type(totalamount))
|
||||
totalamountChildren=0
|
||||
for fm in formset:
|
||||
# print(fm)
|
||||
print(fm['DELETE'].value())
|
||||
if not fm['DELETE'].value() and 'Amount' in fm.cleaned_data:
|
||||
# print(fm.cleaned_data['Amount'])
|
||||
amount=fm.cleaned_data['Amount']
|
||||
# print(amount)
|
||||
print(fm['DELETE'].value())
|
||||
totalamountChildren+=amount
|
||||
print(totalamountChildren)
|
||||
if totalamount==totalamountChildren:
|
||||
print("correct total amount")
|
||||
else:
|
||||
print(f"not correct total amount:{totalamount} supposed={totalamountChildren}")
|
||||
# form.cleaned_data['TotalAmount'] = totalamountChildren
|
||||
parentobj = form.save(commit=False)
|
||||
parentobj.TotalAmount = totalamountChildren
|
||||
parentobj.save()
|
||||
# print(parentobj)
|
||||
print("formset valid")
|
||||
instances = formset.save(commit=False)
|
||||
for obj in formset.deleted_objects:
|
||||
print("deelted object")
|
||||
print(obj)
|
||||
obj.delete()
|
||||
for instance in instances:
|
||||
# print(instance.ItemRefFullName)
|
||||
# print(instance.pk)
|
||||
# print(parentobj.id)
|
||||
instance.Invoice = parentobj
|
||||
instance.save()
|
||||
context['message'] = "All Data Saved"
|
||||
print("all formset saved")
|
||||
print(parentobj.pk, type(parentobj.pk))
|
||||
print(reverse('Invoice:edit_so', args=[parentobj.pk]))
|
||||
messages.success(request, "All Data is Saved")
|
||||
return redirect(reverse('Invoice:edit_so', args=[parentobj.pk]))
|
||||
else:
|
||||
print("formset error")
|
||||
print(formset.errors)
|
||||
else:
|
||||
print("form error")
|
||||
print(form.errors)
|
||||
context['form'] = form
|
||||
context['formset'] = formset
|
||||
return render(request, "Invoice/create-update.html", context=context)
|
||||
|
||||
# def add_so(request):
|
||||
# print("create SO")
|
||||
# context = {}
|
||||
# # obj = get_object_or_404(Invoice, pk=id)
|
||||
# obj=None
|
||||
# form = InvoiceForm(request.POST or None, )
|
||||
# InvoiceItemLineFormset = modelformset_factory(InvoiceItemLine, form=InvoiceItemLineForm, extra=1)
|
||||
# # qs = obj.invoiceitemline_set.all()
|
||||
# qs=None
|
||||
# formset = InvoiceItemLineFormset(request.POST or None, )
|
||||
# context['form'] = form
|
||||
# context['formset'] = formset
|
||||
# context['object'] = obj
|
||||
|
||||
# if all([form.is_valid(), formset.is_valid()]):
|
||||
# print(form.cleaned_data)
|
||||
# for form in formset:
|
||||
# print(form.clean_data)
|
||||
# context['message'] = "All Data saved"
|
||||
# return render(request, "Invoice/create-update.html", context=context)
|
||||
|
||||
|
||||
def getpricelist_so(request, pk=None):
|
||||
print(f"getpricelist INV; pk={pk}")
|
||||
context = {}
|
||||
obj=None
|
||||
plname = None
|
||||
if pk:
|
||||
# obj = get_object_or_404(Invoice.objects.select_related(), pk=pk)
|
||||
obj = get_object_or_404(Customer.objects.select_related(), pk=pk)
|
||||
plname = obj.PriceLevelRefFullName.Name
|
||||
# print(f"{obj.CustomerRefFullName.PriceLevelRefFullName.Name}")
|
||||
# form = InvoiceForm(request.POST or None, instance=obj)
|
||||
# InvoiceItemLineFormset = inlineformset_factory(Invoice, InvoiceItemLine, form=InvoiceItemLineForm, extra=0) #cannot use extra >0 because the formset is zip with the qs below
|
||||
print(obj)
|
||||
qs=None
|
||||
# formset = InvoiceItemLineFormset(request.POST or None)
|
||||
# if pk:
|
||||
# print("got ID")
|
||||
# qs = obj.invoiceitemline_set.all().order_by('id').select_related('ItemRefFullName')
|
||||
# # formset = InvoiceItemLineFormset(request.POST or None, queryset=qs)
|
||||
# formset = InvoiceItemLineFormset(request.POST or None, instance=obj)
|
||||
print(request.GET)
|
||||
# print(len(formset))
|
||||
# print(f"queryset={qs}")
|
||||
# print(f"queryset={qs[0].ItemRefFullName}")
|
||||
# qsItemList = list(x.ItemRefFullName for x in qs)
|
||||
# print(qsItemList)
|
||||
print(f'plname1:{plname}')
|
||||
qsIn = Item.objects.filter(itempricelevel__PL__Name=plname).prefetch_related("itempricelevel").values('id', 'FullName','SalesDesc', 'SalesPrice', 'itempricelevel__Price')
|
||||
qsEx = Item.objects.exclude(itempricelevel__PL__Name=plname).prefetch_related("itempricelevel").values( "id", "FullName", "SalesDesc", "SalesPrice", "AlwaysNull")
|
||||
context['itemdatalist'] = qsIn.union(qsEx)
|
||||
serialized_q = json.dumps(list( context['itemdatalist']), cls=DjangoJSONEncoder)
|
||||
# p=json.loads(serialized_q)
|
||||
print(f'serialized_q:{serialized_q}')
|
||||
serialized_obj = serializers.serialize('json', [ obj, ])
|
||||
context['obj'] = serialized_obj
|
||||
context['itemdatalist'] = serialized_q
|
||||
# serialized_q = serialized_q + serialized_obj
|
||||
# print(serialized_obj)
|
||||
data = json.dumps(context, indent=4, sort_keys=True, default=str)
|
||||
# print(data)
|
||||
# print(serialized_q)
|
||||
return JsonResponse(data, safe=False)
|
||||
|
||||
def edit_so(request):
|
||||
print("Edit SO")
|
||||
context = {}
|
||||
obj = get_object_or_404(Invoice, pk=id)
|
||||
form = InvoiceForm(request.POST or None, instance=obj)
|
||||
InvoiceItemLineFormset = modelformset_factory(InvoiceItemLine, form=InvoiceItemLineForm, extra=1)
|
||||
qs = obj.invoiceitemline_set.all()
|
||||
formset = InvoiceItemLineFormset(request.POST or None, isinstance=qs)
|
||||
context['form'] = form
|
||||
context['formset'] = formset
|
||||
context['object'] = obj
|
||||
|
||||
if all([form.is_valid(), formset.is_valid()]):
|
||||
print(form.cleaned_data)
|
||||
for form in formset:
|
||||
print(form.clean_data)
|
||||
context['message'] = "All Data saved"
|
||||
return render(request, "Invoice/create-update.html", context=context)
|
||||
|
||||
def delete_so(request):
|
||||
pass
|
||||
|
||||
|
||||
0
django/Item/__init__.py
Normal file
0
django/Item/__init__.py
Normal file
19
django/Item/admin.py
Normal file
19
django/Item/admin.py
Normal file
@ -0,0 +1,19 @@
|
||||
from django.contrib import admin
|
||||
from .models import UOM, Item, PriceLevel, PriceLevelItem
|
||||
|
||||
class ItemAdmin(admin.ModelAdmin):
|
||||
list_filter = ("ItemType",)
|
||||
admin.site.register(UOM)
|
||||
admin.site.register(Item, ItemAdmin)
|
||||
|
||||
class PriceLevelPriceLevelItemInline(admin.TabularInline):
|
||||
model = PriceLevelItem
|
||||
readonly_fields = ('id',)
|
||||
extra = 1
|
||||
|
||||
# admin.site.register(PriceLevel)
|
||||
admin.site.register(PriceLevelItem)
|
||||
|
||||
@admin.register(PriceLevel)
|
||||
class PriceLevelAdmin(admin.ModelAdmin):
|
||||
inlines = [PriceLevelPriceLevelItemInline]
|
||||
6
django/Item/apps.py
Normal file
6
django/Item/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ItemConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'Item'
|
||||
44
django/Item/forms.py
Normal file
44
django/Item/forms.py
Normal file
@ -0,0 +1,44 @@
|
||||
from django.forms import ModelForm, Select
|
||||
from django import forms
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Submit, Row, Column, Field
|
||||
from .models import Item, PriceLevel
|
||||
|
||||
class ItemForm(ModelForm):
|
||||
class Meta:
|
||||
model = Item
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
'SalesDesc':forms.Textarea(attrs={'rows':4}),
|
||||
'PurchaseDesc':forms.Textarea(attrs={'rows':4}),
|
||||
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.layout = Layout(
|
||||
# Column('ItemType', css_class='form-group col-md-6 mb-0'),
|
||||
Row(
|
||||
Column('Name', css_class='form-group col-md-4 mb-0'),
|
||||
Column('Parent', css_class='form-group col-md-4 mb-0'),
|
||||
Column('ManufacturerPartNumber', css_class='form-group col-md-4 mb-0'),
|
||||
css_class='form-row'
|
||||
),
|
||||
Field('UnitOfMeasureSetRefFullName', css_class='form-input col-md-4 mb-0'),
|
||||
# 'address_2',
|
||||
Row(
|
||||
# Card
|
||||
Column('SalesDesc', css_class='form-group col-md-6 mb-0'),
|
||||
Column('state', css_class='form-group col-md-4 mb-0'),
|
||||
Column('zip_code', css_class='form-group col-md-2 mb-0'),
|
||||
css_class='form-row'
|
||||
),
|
||||
'check_me_out',
|
||||
Submit('submit', 'Sign in')
|
||||
)
|
||||
|
||||
class PriceLevelForm(ModelForm):
|
||||
class Meta:
|
||||
model = PriceLevel
|
||||
fields = "__all__"
|
||||
0
django/Item/management/commands/__init__.py
Normal file
0
django/Item/management/commands/__init__.py
Normal file
103
django/Item/management/commands/fillitems.py
Normal file
103
django/Item/management/commands/fillitems.py
Normal file
@ -0,0 +1,103 @@
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from Item.models import Item
|
||||
import pandas as pd
|
||||
import numbers as np
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Closes the specified poll for voting"
|
||||
def handle(self, *args, **options):
|
||||
print("fill Items command")
|
||||
|
||||
# return
|
||||
# if request.method == 'POST' and request.FILES['myfile']:
|
||||
# myfile = request.FILES['myfile']
|
||||
# fs = FileSystemStorage()
|
||||
filename = "Item/management/commands/Iteminventory20230417.xls"
|
||||
# filename = "Item/management/commands/Book2.xlsx"
|
||||
# filename = fs.save(myfile.name, myfile)
|
||||
# uploaded_file_url = fs.url(filename)
|
||||
empexceldata = pd.read_excel(filename, converters={'ReorderPoint':int}, )
|
||||
|
||||
dbframe = empexceldata.replace("NaN", "")
|
||||
dbframe = dbframe.fillna("")
|
||||
print(dbframe['PurchaseCost'])
|
||||
dbframe['PurchaseCost'] = dbframe['PurchaseCost'].apply(pd.to_numeric, downcast='float', errors='coerce')
|
||||
dbframe['AverageCost'] = dbframe['AverageCost'].apply(pd.to_numeric, downcast='float', errors='coerce')
|
||||
# dbframe['Reorderpoint'] = dbframe['ReorderPoint'].apply(pd.to_numeric, downcast='float', errors='coerce')
|
||||
# dbframe['ReorderPoint'] = dbframe['ReorderPoint'].astype(int)
|
||||
print (dbframe)
|
||||
dbframe['PurchaseCost'] = dbframe['PurchaseCost'].fillna(0)
|
||||
dbframe['AverageCost'] = dbframe['AverageCost'].fillna(0)
|
||||
print(dbframe['PurchaseCost'])
|
||||
# print(dbframe['NameFromTaco'])
|
||||
# print(dbframe['ReorderPoint'])
|
||||
print(dbframe['QuantityOnHand'])
|
||||
print (dbframe.dtypes)
|
||||
icount=0
|
||||
for db in dbframe.itertuples():
|
||||
obj, created = Item.objects.get_or_create(FullName=db.FullName)
|
||||
print(obj.FullName, obj.pk)
|
||||
# if created:
|
||||
obj.ItemType=1
|
||||
obj.Name=db.Name
|
||||
# obj.FullName=db.FullName
|
||||
# obj.# BarCodeValue=db.# BarCodeValue
|
||||
obj.SalesDesc=db.SalesDesc
|
||||
# obj.# SalesPrice=db.# SalesPrice
|
||||
obj.PurchaseDesc=db.PurchaseDesc
|
||||
obj.Sublevel=db.Sublevel
|
||||
obj.ManufacturerPartNumber=db.ManufacturerPartNumber
|
||||
obj.PurchaseCost=db.PurchaseCost
|
||||
# obj.ReorderPoint=db.ReorderPoint
|
||||
obj.QuantityOnHand=db.QuantityOnHand
|
||||
obj.AverageCost=db.AverageCost
|
||||
obj.QuantityOnOrder=db.QuantityOnOrder
|
||||
obj.QuantityOnSalesOrder=db.QuantityOnSalesOrder
|
||||
# obj.# UnitOfMeasureSetRefFullName=db.# UnitOfMeasureSetRefFullName
|
||||
obj.ParentRefFullName=db.ParentRefFullName
|
||||
# obj.# SalesTaxCodeRefFullName=db.# SalesTaxCodeRefFullName
|
||||
obj.IncomeAccountRefFullName=db.IncomeAccountRefFullName
|
||||
obj.COGSAccountRefFullName=db.COGSAccountRefFullName
|
||||
obj.PrefVendorRefFullName=db.PrefVendorRefFullName
|
||||
obj.AssetAccountRefFullName=db.AssetAccountRefFullName
|
||||
obj.IsActive=db.IsActive
|
||||
obj.NameFromTaco=db.NameFromTaco
|
||||
obj.CATEGORY=db.CATEGORY
|
||||
obj.Type=db.Type
|
||||
|
||||
# obj = Item.objects.create(Name = db.Name,
|
||||
# FullName = db.FullName,
|
||||
# # BarCodeValue = db.# BarCodeValue,
|
||||
# SalesDesc = db.SalesDesc,
|
||||
# # SalesPrice = db.# SalesPrice,
|
||||
# PurchaseDesc = db.PurchaseDesc,
|
||||
# Sublevel = db.Sublevel,
|
||||
# ManufacturerPartNumber = db.ManufacturerPartNumber,
|
||||
# PurchaseCost = db.PurchaseCost,
|
||||
# # ReorderPoint = db.ReorderPoint,
|
||||
# QuantityOnHand = db.QuantityOnHand,
|
||||
# AverageCost = db.AverageCost,
|
||||
# QuantityOnOrder = db.QuantityOnOrder,
|
||||
# QuantityOnSalesOrder = db.QuantityOnSalesOrder,
|
||||
# # UnitOfMeasureSetRefFullName = db.# UnitOfMeasureSetRefFullName,
|
||||
# ParentRefFullName = db.ParentRefFullName,
|
||||
# # SalesTaxCodeRefFullName = db.# SalesTaxCodeRefFullName,
|
||||
# IncomeAccountRefFullName = db.IncomeAccountRefFullName,
|
||||
# COGSAccountRefFullName = db.COGSAccountRefFullName,
|
||||
# PrefVendorRefFullName = db.PrefVendorRefFullName,
|
||||
# AssetAccountRefFullName = db.AssetAccountRefFullName,
|
||||
# IsActive = db.IsActive,
|
||||
# NameFromTaco = db.NameFromTaco,
|
||||
# CATEGORY = db.CATEGORY,
|
||||
# Type = db.Type,
|
||||
# )
|
||||
obj.save()
|
||||
icount+=1
|
||||
print(f"{icount} Records updated/added")
|
||||
|
||||
|
||||
|
||||
# return render(request, 'Import_excel_db.html', {
|
||||
# 'uploaded_file_url': uploaded_file_url
|
||||
# })
|
||||
# return render(request, 'Import_excel_db.html',{})
|
||||
60
django/Item/migrations/0001_initial.py
Normal file
60
django/Item/migrations/0001_initial.py
Normal file
@ -0,0 +1,60 @@
|
||||
# Generated by Django 4.2 on 2023-05-02 15:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UOM',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('Name', models.CharField(max_length=80)),
|
||||
('Abbreviation', models.CharField(max_length=12)),
|
||||
('Number_of', models.DecimalField(decimal_places=3, max_digits=12)),
|
||||
('Parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parent', to='Item.uom')),
|
||||
('Purchases', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='purchases', to='Item.uom')),
|
||||
('Sales', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='sales', to='Item.uom')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Item',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ItemType', models.CharField(choices=[('3', 'SERVICE'), ('1', 'INVENTORY PART'), ('2', 'NON INVENTORY'), ('4', 'OTHER CHARGES'), ('5', 'SUB TOTAL'), ('6', 'GROUP'), ('7', 'DISCOUNT'), ('8', 'PAYMENT')], default='3', max_length=1)),
|
||||
('Name', models.CharField(max_length=80)),
|
||||
('FullName', models.CharField(blank=True, max_length=80, null=True, unique=True)),
|
||||
('SalesDesc', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('SalesPrice', models.DecimalField(decimal_places=2, default=0, max_digits=10)),
|
||||
('PurchaseDesc', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Sublevel', models.PositiveSmallIntegerField(default=0)),
|
||||
('ManufacturerPartNumber', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('PurchaseCost', models.DecimalField(decimal_places=2, default=0, max_digits=10)),
|
||||
('ReorderPoint', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||
('QuantityOnHand', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||
('AverageCost', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||
('QuantityOnOrder', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||
('QuantityOnSalesOrder', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
|
||||
('ParentRefFullName', models.CharField(max_length=80)),
|
||||
('IncomeAccountRefFullName', models.CharField(default='Sales', max_length=80)),
|
||||
('COGSAccountRefFullName', models.CharField(default='COGS', max_length=80)),
|
||||
('PrefVendorRefFullName', models.CharField(default='TACO', max_length=80)),
|
||||
('AssetAccountRefFullName', models.CharField(default='Inventory Asset', max_length=80)),
|
||||
('IsActive', models.BooleanField(default=True)),
|
||||
('NameFromTaco', models.CharField(blank=True, max_length=60, null=True)),
|
||||
('CATEGORY', models.CharField(blank=True, max_length=60, null=True)),
|
||||
('Type', models.CharField(blank=True, max_length=60, null=True)),
|
||||
('TimeCreated', models.DateTimeField(auto_now_add=True)),
|
||||
('TimeModified', models.DateTimeField(auto_now=True)),
|
||||
('Parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='Item.item')),
|
||||
('UnitOfMeasureSetRefFullName', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='Item.uom')),
|
||||
],
|
||||
),
|
||||
]
|
||||
18
django/Item/migrations/0002_alter_item_parentreffullname.py
Normal file
18
django/Item/migrations/0002_alter_item_parentreffullname.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-02 15:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='item',
|
||||
name='ParentRefFullName',
|
||||
field=models.CharField(blank=True, max_length=80, null=True),
|
||||
),
|
||||
]
|
||||
18
django/Item/migrations/0003_alter_uom_number_of.py
Normal file
18
django/Item/migrations/0003_alter_uom_number_of.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-03 13:13
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0002_alter_item_parentreffullname'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='uom',
|
||||
name='Number_of',
|
||||
field=models.DecimalField(blank=True, decimal_places=3, max_digits=12, null=True),
|
||||
),
|
||||
]
|
||||
18
django/Item/migrations/0004_alter_uom_name.py
Normal file
18
django/Item/migrations/0004_alter_uom_name.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-03 13:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0003_alter_uom_number_of'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='uom',
|
||||
name='Name',
|
||||
field=models.CharField(blank=True, max_length=80, null=True),
|
||||
),
|
||||
]
|
||||
18
django/Item/migrations/0005_alter_item_itemtype.py
Normal file
18
django/Item/migrations/0005_alter_item_itemtype.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-04 16:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0004_alter_uom_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='item',
|
||||
name='ItemType',
|
||||
field=models.CharField(choices=[('3', 'SERVICE'), ('1', 'INVENTORY PART'), ('2', 'NON INVENTORY'), ('4', 'OTHER CHARGES'), ('5', 'SUB TOTAL'), ('6', 'GROUP'), ('7', 'DISCOUNT'), ('8', 'PAYMENT')], max_length=1),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2 on 2023-05-04 20:53
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0005_alter_item_itemtype'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='item',
|
||||
name='UnitOfMeasureSetRefFullName',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='Item.uom', verbose_name='UOM'),
|
||||
),
|
||||
]
|
||||
35
django/Item/migrations/0007_pricelevel_pricelevelitem.py
Normal file
35
django/Item/migrations/0007_pricelevel_pricelevelitem.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Generated by Django 4.2 on 2023-05-08 05:20
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0006_alter_item_unitofmeasuresetreffullname'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PriceLevel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('Name', models.CharField(max_length=50)),
|
||||
('Desc', models.CharField(blank=True, max_length=180, null=True, verbose_name='Description')),
|
||||
('TimeCreated', models.DateTimeField(auto_now_add=True)),
|
||||
('TimeModified', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PriceLevelItem',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('Price', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||
('TimeCreated', models.DateTimeField(auto_now_add=True)),
|
||||
('TimeModified', models.DateTimeField(auto_now=True)),
|
||||
('IPL', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='itempricelevel', to='Item.item')),
|
||||
('PL', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pricelevel', to='Item.pricelevel')),
|
||||
],
|
||||
),
|
||||
]
|
||||
19
django/Item/migrations/0008_alter_pricelevelitem_pl.py
Normal file
19
django/Item/migrations/0008_alter_pricelevelitem_pl.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2 on 2023-05-08 05:22
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0007_pricelevel_pricelevelitem'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='pricelevelitem',
|
||||
name='PL',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricelevel', to='Item.pricelevel'),
|
||||
),
|
||||
]
|
||||
18
django/Item/migrations/0009_item_alwaysnull.py
Normal file
18
django/Item/migrations/0009_item_alwaysnull.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-08 22:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0008_alter_pricelevelitem_pl'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='AlwaysNull',
|
||||
field=models.CharField(blank=True, editable=False, max_length=1, null=True),
|
||||
),
|
||||
]
|
||||
18
django/Item/migrations/0010_alter_pricelevelitem_price.py
Normal file
18
django/Item/migrations/0010_alter_pricelevelitem_price.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-10 20:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Item', '0009_item_alwaysnull'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='pricelevelitem',
|
||||
name='Price',
|
||||
field=models.DecimalField(decimal_places=2, max_digits=12, null=True),
|
||||
),
|
||||
]
|
||||
0
django/Item/migrations/__init__.py
Normal file
0
django/Item/migrations/__init__.py
Normal file
140
django/Item/models.py
Normal file
140
django/Item/models.py
Normal file
@ -0,0 +1,140 @@
|
||||
from django.db import models
|
||||
from django.db import IntegrityError
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.urls import reverse
|
||||
|
||||
class PriceLevel(models.Model):
|
||||
Name = models.CharField(max_length=50)
|
||||
Desc = models.CharField(max_length=180, verbose_name="Description", blank=True, null=True)
|
||||
TimeCreated = models.DateTimeField(auto_now_add=True)
|
||||
TimeModified = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.pk}; {self.Name}"
|
||||
|
||||
def get_field_name(obj):
|
||||
# return [(f.name, f.value_to_string(obj)) for f in obj._meta.fields] #get value convert it to string
|
||||
return [(f.verbose_name, f.name, f.value_from_object(obj)) for f in obj._meta.fields]
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('Item:edit_pricelevel', args=[str(self.id)])
|
||||
|
||||
class PriceLevelItem(models.Model):
|
||||
PL = models.ForeignKey("PriceLevel", related_name="pricelevel", on_delete=models.CASCADE)
|
||||
IPL = models.ForeignKey("Item", related_name="itempricelevel", on_delete=models.CASCADE)
|
||||
Price = models.DecimalField(max_digits=12, decimal_places=2, null=True)
|
||||
TimeCreated = models.DateTimeField(auto_now_add=True)
|
||||
TimeModified = models.DateTimeField(auto_now=True)
|
||||
|
||||
#class meta pl ipl unique together
|
||||
def __str__(self):
|
||||
return f"{self.PL.Name}; {self.IPL.FullName}; {self.Price}"
|
||||
|
||||
class UOM(models.Model):
|
||||
Name = models.CharField(max_length=80, blank=True, null=True)
|
||||
Abbreviation = models.CharField(max_length=12)
|
||||
Number_of = models.DecimalField(max_digits=12, decimal_places=3, blank=True, null=True)
|
||||
Parent = models.ForeignKey("UOM", related_name="parent", on_delete=models.DO_NOTHING, blank=True, null=True)
|
||||
Purchases = models.ForeignKey("UOM", related_name="purchases", on_delete=models.DO_NOTHING, blank=True, null=True)
|
||||
Sales = models.ForeignKey("UOM", related_name="sales", on_delete=models.DO_NOTHING, blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.Abbreviation
|
||||
|
||||
class Item(models.Model):
|
||||
ITEMINVENTORY = "1"
|
||||
NONINVENTORY = "2"
|
||||
SERVICE = "3"
|
||||
OTHERCHARGES = "4"
|
||||
SUBTOTAL = "5"
|
||||
GROUP = "6"
|
||||
DISCOUNT = "7"
|
||||
PAYMENT = "8"
|
||||
ITEM_TYPE_CHOICES = [
|
||||
(SERVICE, "SERVICE"),
|
||||
(ITEMINVENTORY, "INVENTORY PART"),
|
||||
(NONINVENTORY, "NON INVENTORY"),
|
||||
(OTHERCHARGES, "OTHER CHARGES"),
|
||||
(SUBTOTAL, "SUB TOTAL"),
|
||||
(GROUP, "GROUP"),
|
||||
(DISCOUNT, "DISCOUNT"),
|
||||
(PAYMENT, "PAYMENT"),
|
||||
]
|
||||
ItemType = models.CharField(max_length=1, choices=ITEM_TYPE_CHOICES)
|
||||
Name = models.CharField(max_length=80)
|
||||
Parent = models.ForeignKey("Item", on_delete=models.DO_NOTHING, blank=True, null=True)
|
||||
FullName = models.CharField(max_length=80, blank=True, null=True, unique=True)
|
||||
# BarCodeValue = models.CharField(max_length=80)
|
||||
SalesDesc = models.CharField(max_length=80, blank=True, null=True)
|
||||
SalesPrice = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
||||
PurchaseDesc = models.CharField(max_length=80, blank=True, null=True)
|
||||
Sublevel = models.PositiveSmallIntegerField(default = 0)
|
||||
ManufacturerPartNumber = models.CharField(max_length=80, blank=True, null=True)
|
||||
PurchaseCost = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
||||
ReorderPoint = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
|
||||
QuantityOnHand = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
|
||||
AverageCost = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
|
||||
QuantityOnOrder = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
|
||||
QuantityOnSalesOrder = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
|
||||
UnitOfMeasureSetRefFullName = models.ForeignKey(UOM, verbose_name="UOM", on_delete=models.DO_NOTHING, blank=True, null=True)
|
||||
ParentRefFullName = models.CharField(max_length=80, blank=True, null=True)
|
||||
# SalesTaxCodeRefFullName
|
||||
IncomeAccountRefFullName= models.CharField(max_length=80, default="Sales")
|
||||
COGSAccountRefFullName = models.CharField(max_length=80, default="COGS")
|
||||
PrefVendorRefFullName = models.CharField(max_length=80, default="TACO")
|
||||
AssetAccountRefFullName = models.CharField(max_length=80, default="Inventory Asset")
|
||||
IsActive = models.BooleanField(default=True)
|
||||
NameFromTaco = models.CharField(max_length=60, blank=True, null=True)
|
||||
CATEGORY = models.CharField(max_length=60, blank=True, null=True)
|
||||
Type = models.CharField(max_length=60, blank=True, null=True)
|
||||
AlwaysNull = models.CharField(max_length=1, blank=True, null=True, editable=False)
|
||||
TimeCreated = models.DateTimeField(auto_now_add=True)
|
||||
TimeModified = models.DateTimeField(auto_now=True)
|
||||
|
||||
# class Meta:
|
||||
# ordering = ["FullName"]
|
||||
|
||||
def get_field_name(obj):
|
||||
# return [(f.name, f.value_to_string(obj)) for f in obj._meta.fields] #get value convert it to string
|
||||
return [(f.verbose_name, f.name, f.value_from_object(obj)) for f in obj._meta.fields]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
try:
|
||||
self.FullName = self.Name
|
||||
print(f"self.FullName={self.FullName}; self.parentreffullname={self.ParentRefFullName}; self.pk={self.pk}")
|
||||
if self.ParentRefFullName:
|
||||
print("masuk")
|
||||
parent = Item.objects.filter(FullName__iexact=self.ParentRefFullName)[0]
|
||||
print(f"parent.fullname:{parent.FullName}")
|
||||
|
||||
if parent:
|
||||
print(parent.FullName)
|
||||
self.Parent=parent
|
||||
print(f"self.Parent={self.Parent}")
|
||||
if self.Parent:
|
||||
print(f"self.Parent={self.Parent}")
|
||||
if self.Parent.FullName:
|
||||
self.FullName = f"{self.Parent.FullName}:{self.Name}" #no need to do upper()
|
||||
else:
|
||||
self.FullName = f"{self.Parent.Name}:{self.Name}"
|
||||
print(self.Parent.Name, self.Parent.FullName, self.FullName)
|
||||
item = Item.objects.filter(FullName__iexact=self.FullName)
|
||||
if item and not self.pk: #check for unique constrain
|
||||
raise ValidationError("Item FullName should be Unique") #TODO again later
|
||||
# return
|
||||
print("raise validationerror itemfullname should unique")
|
||||
print(f"{self.IsActive}; {self.PurchaseCost}; {self.ReorderPoint}")
|
||||
super(Item, self).save(*args, **kwargs)
|
||||
except IntegrityError:
|
||||
print("integrity error")
|
||||
except ValidationError:
|
||||
print("valid error")
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.FullName:
|
||||
return f"{self.FullName}"
|
||||
else:
|
||||
return self.Name
|
||||
|
||||
def get_absolute_url(self): # // ---> new function
|
||||
return reverse('Item:edit_item', args=[str(self.id)])
|
||||
101
django/Item/templates/Item/addedit_item.html
Normal file
101
django/Item/templates/Item/addedit_item.html
Normal file
@ -0,0 +1,101 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{{title}}{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{% comment %} {% crispy form %} {% endcomment %}
|
||||
{% comment %} {{form|crispy}} {% endcomment %}
|
||||
<div class='container mt-3'>
|
||||
<div class='row ms-0'>
|
||||
<div class='card col-6 mb-2'>
|
||||
<div class='row form-row ms-0 me-0'>
|
||||
<div class='form-input col-4 col-md-4 mb-0'>{{form.ItemType|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='col-4 col-offset-2'>
|
||||
<div class='form-input col-4 col-md-4 mb-0 mt-3 ms-5'>{{form.IsActive|as_crispy_field}}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class='card mb-2'>
|
||||
<div class='row form-row ms-0 me-0'>
|
||||
<div class='form-group col-md-4 mb-0'> {{form.Name|as_crispy_field}}</div>
|
||||
<div class='form-group col-md-4 mb-0'> {{form.Parent|as_crispy_field}}</div>
|
||||
<div class='form-group col-md-4 mb-0'> {{form.ManufacturerPartNumber|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='card col-6 mb-2'>
|
||||
<div class='row form-row ms-0 me-0'>
|
||||
<div class='form-input col-4 col-md-4 mb-0'>{{form.UnitOfMeasureSetRefFullName|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='card-group mb-2'>
|
||||
<div class='card '>
|
||||
<div class='row form-row ms-0 me-0'>
|
||||
<div class'form-group col-md-4 mb-0'>{{form.PurchaseDesc|as_crispy_field}}</div>
|
||||
<div class'form-group col-md-4 mb-0'>{{form.PurchaseCost|as_crispy_field}}</div>
|
||||
<div class'form-group col-md-4 mb-0'>{{form.COGSAccountRefFullName|as_crispy_field}}</div>
|
||||
<div class'form-group col-md-4 mb-0'>{{form.PrefVendorRefFullName|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='card'>
|
||||
<div class='row form-row ms-0 me-0'>
|
||||
<div class'form-group col-md-4 mb-0'>{{form.SalesDesc|as_crispy_field}}</div>
|
||||
<div class'form-group col-md-4 mb-0'>{{form.SalesPrice|as_crispy_field}}</div>
|
||||
<div></div>
|
||||
<div class'form-group col-md-4 mb-0'>{{form.IncomeAccountRefFullName|as_crispy_field}}</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='card mb-2'>
|
||||
<div class='row form-row ms-0 me-0'>
|
||||
<div class='form-group col-md-2 mb-0'> {{form.IncomeAccountRefFullName|as_crispy_field}}</div>
|
||||
<div class='form-group col-md mb-0'> {{form.ReorderPoint|as_crispy_field}}</div>
|
||||
<div class='form-group col-md mb-0'> {{form.ReorderPoint|as_crispy_field}}</div>
|
||||
<div class='form-group col-md mb-0'> {{form.QuantityOnHand|as_crispy_field}}</div>
|
||||
<div class='form-group col-md mb-0'> {{form.AverageCost|as_crispy_field}}</div>
|
||||
<div class='form-group col-md mb-0'> {{form.QuantityOnOrder|as_crispy_field}}</div>
|
||||
<div class='form-group col-md mb-0'> {{form.QuantityOnSalesOrder|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class='btn btn-primary mb-3'>submit</button>
|
||||
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#staticBackdrop">
|
||||
Launch static backdrop modal
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="staticBackdropLabel">Modal title</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class='row'>
|
||||
<div class='col-8'>
|
||||
<div class='form-group col mb-0'> {{form.NameFromTaco|as_crispy_field}}</div>
|
||||
<div class='form-group col mb-0'> {{form.CATEGORY|as_crispy_field}}</div>
|
||||
<div class='form-group col mb-0'> {{form.Type|as_crispy_field}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Understood</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
{% endblock body %}
|
||||
|
||||
173
django/Item/templates/Item/addedit_pricelevel.html
Normal file
173
django/Item/templates/Item/addedit_pricelevel.html
Normal file
@ -0,0 +1,173 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Price Level{% endblock title %}
|
||||
|
||||
{% block search_nav %}
|
||||
<form class="d-flex" role="search" action="" method="GET">
|
||||
<input autocomplete=off id="search" list="itemname" class="form-control me-2" type="search" placeholder="Search" aria-label="Search" name="q">
|
||||
<button class="btn btn-outline-success" type="submit" onclick="search_btn(event)">Search</button>
|
||||
</form>
|
||||
<datalist id="itemname">
|
||||
{% with objects as items %}
|
||||
{% for item in items %}
|
||||
{% comment %} {{item.FullName}} {% endcomment %}
|
||||
<option value="{{item.FullName}}">{{item.FullName}}</option>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
{% endblock search_nav %}
|
||||
|
||||
{% block body %}
|
||||
<form method="POST" onkeydown="return event.key != 'Enter';">
|
||||
{% csrf_token %}
|
||||
{{form|crispy}}
|
||||
<div class="table-responsive">
|
||||
<table id="tableId" class="table-sm table-scroll table-bordered border-info table-hover table-striped resizable">
|
||||
<thead><tr>
|
||||
<th>FullName</th>
|
||||
<th>SalesPrice</th>
|
||||
<th>Price</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for obj in objects %}
|
||||
<tr>
|
||||
<td><input type="text" disabled="True" style='width:275px' value="{{obj.FullName}}"></td>
|
||||
<td><input type="text" disabled="True" value={{obj.SalesPrice}}></td>
|
||||
<td><input autocomplete=off class="pricelist" type="text" onfocusout="focusout(this)" data-changed data-fullname="{{obj.FullName}}"
|
||||
{% if obj.itempricelevel__Price %}
|
||||
data-ori={{obj.itempricelevel__Price}} value={{obj.itempricelevel__Price}}
|
||||
{% else %}
|
||||
data-ori=""
|
||||
{% endif %}>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button type="submit" onclick="clicksubmit(event)">submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function search_btn(e) {
|
||||
e.preventDefault();
|
||||
console.log("search button clicked");
|
||||
let searchval = document.getElementById('search');
|
||||
//const searchele=document.querySelector('option[value="' + e.target.value + '"]');
|
||||
var searchele=document.querySelector('input[data-fullname="' + searchval.value + '"]')
|
||||
var searcheleparent=document.querySelector('input[data-fullname="' + searchval.value + '"]').parentNode.parentNode;
|
||||
var prevSibling=searcheleparent.previousElementSibling;
|
||||
if (prevSibling) {
|
||||
prevSibling.scrollIntoView();
|
||||
}
|
||||
searchele.select();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const pk = {{pk}};
|
||||
const datajson={};
|
||||
const plist = document.getElementsByClassName('pricelist');
|
||||
//console.log(plist.length);
|
||||
//console.log(pk);
|
||||
const idname = document.getElementById('id_Name');
|
||||
const iddesc = document.getElementById('id_Desc');
|
||||
//console.log(idname);
|
||||
function focusout(e){
|
||||
console.log("data-changed="+ e.getAttribute('data-changed')+";");
|
||||
if ((parseFloat(e.value).toFixed(2)!=e.getAttribute('data-ori') && e.value != "") ) {
|
||||
//if (e.getAttribute('data-ori')) {
|
||||
if (e.dataset.ori!=""){
|
||||
e.setAttribute('data-changed', "2"); // 2 => EDIT
|
||||
console.log("edit " + e.getAttribute('data-fullname') + ";" + e.getAttribute('data-ori') + "!= "+e.value);
|
||||
console.log("data-changed="+ e.getAttribute('data-changed')+";");
|
||||
}
|
||||
else {
|
||||
//e.setAttribute('data-changed', "1"); // thesame thing with below syntax
|
||||
e.dataset.changed = 1; // 1=> ADD NEW
|
||||
console.log("add " + e.getAttribute('data-fullname') + ";" + e.getAttribute('data-ori') + "!= "+e.value);
|
||||
}
|
||||
} else if ((e.value == null || e.value == "") && e.dataset.ori!=""){
|
||||
console.log("dtchange");
|
||||
e.setAttribute('data-changed', "2"); // 2 => EDIT
|
||||
console.log("edit " + e.getAttribute('data-fullname') + ";" + e.getAttribute('data-ori') + "!= "+e.value);
|
||||
} else if (e.dataset.changed) {
|
||||
e.dataset.changed = undefined;
|
||||
e.removeAttribute('data-changed');
|
||||
console.log(e.getAttribute('data-fullname') + " back to ori " );
|
||||
}
|
||||
console.log("data-changed="+ e.getAttribute('data-changed')+";");
|
||||
}
|
||||
|
||||
function clicksubmit(e){
|
||||
e.preventDefault();
|
||||
//console.log(pk);
|
||||
//console.log(e);
|
||||
|
||||
let dt = [];
|
||||
for (let i = 0; i < plist.length; i++) {
|
||||
let inputprice = plist[i].value;
|
||||
let dtchange = plist[i].dataset.changed;
|
||||
let dtfullname = plist[i].dataset.fullname;
|
||||
if (dtchange) {
|
||||
let dttemp = {
|
||||
FullName : dtfullname,
|
||||
dtchange : dtchange,
|
||||
price : parseFloat(parseFloat(inputprice).toFixed(2)),
|
||||
pk : pk
|
||||
};
|
||||
//console.log(dttemp);
|
||||
dt.push(dttemp);
|
||||
}
|
||||
}
|
||||
console.log(dt);
|
||||
const datajson = JSON.stringify(dt);
|
||||
console.log(datajson);
|
||||
sendform(datajson, idname.value, iddesc.value);
|
||||
console.log("data sent");
|
||||
}
|
||||
|
||||
function sendform(data, name, desc) {
|
||||
fetch('', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': csrftoken,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
'data': data,
|
||||
'pk' : pk,
|
||||
'Name' : name,
|
||||
'Desc' : desc,
|
||||
}),
|
||||
redirect: "follow", // follow, manual, error
|
||||
})
|
||||
.then(response => {
|
||||
if (response.redirected) {
|
||||
window.location.replace(response.url);
|
||||
// creates the second request, and change the content
|
||||
return;
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const 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;
|
||||
}
|
||||
const csrftoken = getCookie('csrftoken');
|
||||
console.log(csrftoken);
|
||||
</script>
|
||||
|
||||
{% endblock body %}
|
||||
1
django/Item/templates/Item/index.html
Normal file
1
django/Item/templates/Item/index.html
Normal file
@ -0,0 +1 @@
|
||||
{% include "Item/table_index.html" with title="Item Index" %}
|
||||
1
django/Item/templates/Item/index_pricelevel.html
Normal file
1
django/Item/templates/Item/index_pricelevel.html
Normal file
@ -0,0 +1 @@
|
||||
{% include 'Item/table_index.html' with title="Price Level Index" %}
|
||||
221
django/Item/templates/Item/table_index.html
Normal file
221
django/Item/templates/Item/table_index.html
Normal file
@ -0,0 +1,221 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load tz %}
|
||||
|
||||
{% block title %}
|
||||
{{title}}
|
||||
{% endblock title %}
|
||||
|
||||
{% block search_nav %}
|
||||
<form class="d-flex" role="search" action="" method="GET">
|
||||
<input autocomplete=off onfocusout="focusout(this)" id="search" list="itemname" class="form-control me-2" type="search" placeholder="Search" aria-label="Search" name="q">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
<datalist id="itemname">
|
||||
{% for item in items %}
|
||||
{{item.FullName}}
|
||||
<option value="{{item.FullName}}"><a href="{{item.get_absolute_url}}">{{item.FullName}}; {{item.SalesDesc}}; {{item.SalesPrice}}</a></option>"
|
||||
{% endfor %}
|
||||
</datalist>
|
||||
{% endblock search_nav %}
|
||||
|
||||
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<div>
|
||||
<a href="{{addurl}}" class="btn btn-primary mt-2">Add New</a>
|
||||
</div>
|
||||
<div class="table-responsive mt-2">
|
||||
<table id="tableId" class=" table table-sm table-scroll table-bordered border-info table-hover table-striped resizable">
|
||||
<thead><tr>
|
||||
{% for verbose_name, key, val in objects.0.get_field_name %}
|
||||
<th>{{verbose_name}}</th>
|
||||
{% endfor %}
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for object in objects %}
|
||||
<tr>
|
||||
{% for vn, key, val in object.get_field_name %}
|
||||
{% if forloop.first %}
|
||||
{% comment %} <td><a href="{% url 'edit_item' object.pk %}">{{object.Name|safe}}</a></td> {% endcomment %}
|
||||
<td><a href="{{ object.get_absolute_url }}">
|
||||
{% if object.Name %}
|
||||
{{object.Name|safe}}
|
||||
{% elif object.CustomerName %}
|
||||
{{object.CustomerName}}
|
||||
{% elif object.RefNumber %}
|
||||
{{object.RefNumber}}
|
||||
{% endif %}
|
||||
</a></td>
|
||||
{% else %}
|
||||
{% if key == "TimeModified" or key == "TimeCreated" %}
|
||||
<td>{{val|localtime}}</td>
|
||||
{% elif key == "ItemType" %}
|
||||
<td>{{object.get_ItemType_display}}</td>
|
||||
{% else %}
|
||||
<td>{{val}}</td>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% if objects.paginator.num_pages > 1 %}
|
||||
<div class = "d-flex justify-content-center aligns-items-center">
|
||||
<div class="pagination ">
|
||||
<span class="step-links">
|
||||
{% if objects.has_previous %}
|
||||
<a href="?page=1{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">« first</a>
|
||||
<a href="?page={{ objects.previous_page_number }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">previous</a>
|
||||
{% endif %}
|
||||
|
||||
<span class="current">
|
||||
Page {{ objects.number }} of {{ objects.paginator.num_pages }}.
|
||||
</span>
|
||||
|
||||
{% if objects.has_next %}
|
||||
<a href="?page={{ objects.next_page_number }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">next</a>
|
||||
<a href="?page={{ objects.paginator.num_pages }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">last »</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="result">
|
||||
test
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
const search = document.getElementById('search');
|
||||
const result = document.getElementById('result');
|
||||
const itemname = document.getElementById('itemname');
|
||||
function focusout(e){
|
||||
let Text = document.querySelector('option[value="' + e.value + '"]');
|
||||
if (Text){
|
||||
console.log(e.value)
|
||||
console.log(Text.text)
|
||||
}
|
||||
else {
|
||||
console.log("element not exist")
|
||||
}
|
||||
|
||||
}
|
||||
const inputHandler = function(e) {
|
||||
console.log(e.target.Text)
|
||||
result.innerHTML = e.target.value;
|
||||
}
|
||||
|
||||
search.addEventListener('input', inputHandler);
|
||||
search.addEventListener('propertychange', inputHandler);
|
||||
</script>
|
||||
|
||||
<script>
|
||||
//script to rezise columns TABLE
|
||||
//var tables = document.getElementsByClassName('flexiCol');
|
||||
var tables = document.getElementsByClassName('resizable');
|
||||
for (var i = 0; i < tables.length; i++) {
|
||||
//console.log(tables[i]);
|
||||
resizableGrid(tables[i]);
|
||||
}
|
||||
|
||||
function resizableGrid(table) {
|
||||
var row = table.getElementsByTagName('tr')[0],
|
||||
cols = row ? row.children : undefined;
|
||||
if (!cols) return;
|
||||
table.style.overflow = 'hidden';
|
||||
|
||||
var tableHeight = table.offsetHeight;
|
||||
|
||||
for (var i = 0; i < cols.length; i++) {
|
||||
var div = createDiv(tableHeight);
|
||||
cols[i].appendChild(div);
|
||||
cols[i].style.position = 'relative';
|
||||
setListeners(div);
|
||||
}
|
||||
|
||||
function setListeners(div) {
|
||||
var pageX, curCol, nxtCol, curColWidth, nxtColWidth, tableWidth;
|
||||
|
||||
div.addEventListener('mousedown', function(e) {
|
||||
|
||||
tableWidth = document.getElementById('tableId').offsetWidth;
|
||||
curCol = e.target.parentElement;
|
||||
nxtCol = curCol.nextElementSibling;
|
||||
pageX = e.pageX;
|
||||
|
||||
var padding = paddingDiff(curCol);
|
||||
|
||||
curColWidth = curCol.offsetWidth - padding;
|
||||
// if (nxtCol)
|
||||
//nxtColWidth = nxtCol.offsetWidth - padding;
|
||||
});
|
||||
|
||||
div.addEventListener('mouseover', function(e) {
|
||||
e.target.style.borderRight = '2px solid #0000ff';
|
||||
})
|
||||
|
||||
div.addEventListener('mouseout', function(e) {
|
||||
e.target.style.borderRight = '';
|
||||
})
|
||||
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
if (curCol) {
|
||||
var diffX = e.pageX - pageX;
|
||||
|
||||
// if (nxtCol)
|
||||
//nxtCol.style.width = (nxtColWidth - (diffX)) + 'px';
|
||||
|
||||
curCol.style.width = (curColWidth + diffX) + 'px';
|
||||
console.log(curCol.style.width)
|
||||
console.log(tableWidth)
|
||||
document.getElementById('tableId').style.width = tableWidth + diffX + "px"
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', function(e) {
|
||||
curCol = undefined;
|
||||
nxtCol = undefined;
|
||||
pageX = undefined;
|
||||
nxtColWidth = undefined;
|
||||
curColWidth = undefined
|
||||
});
|
||||
}
|
||||
|
||||
function createDiv(height) {
|
||||
var div = document.createElement('div');
|
||||
div.style.top = 0;
|
||||
div.style.right = 0;
|
||||
div.style.width = '5px';
|
||||
div.style.position = 'absolute';
|
||||
div.style.cursor = 'col-resize';
|
||||
div.style.userSelect = 'none';
|
||||
div.style.height = height + 'px';
|
||||
return div;
|
||||
}
|
||||
|
||||
function paddingDiff(col) {
|
||||
|
||||
if (getStyleVal(col, 'box-sizing') == 'border-box') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var padLeft = getStyleVal(col, 'padding-left');
|
||||
var padRight = getStyleVal(col, 'padding-right');
|
||||
return (parseInt(padLeft) + parseInt(padRight));
|
||||
|
||||
}
|
||||
|
||||
function getStyleVal(elm, css) {
|
||||
return (window.getComputedStyle(elm, null).getPropertyValue(css))
|
||||
}
|
||||
};
|
||||
</script>
|
||||
{% endblock body %}
|
||||
3
django/Item/tests.py
Normal file
3
django/Item/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
14
django/Item/urls.py
Normal file
14
django/Item/urls.py
Normal file
@ -0,0 +1,14 @@
|
||||
from django.urls import path, include
|
||||
from .views import index, add_item, edit_item, delete_item, edit_pricelevel, add_pricelevel, index_pricelevel
|
||||
app_name = "Item"
|
||||
urlpatterns = [
|
||||
|
||||
path('', index, name="index"),
|
||||
path('add', add_item, name="add_item"),
|
||||
path('edit/<int:pk>', edit_item, name="edit_item"),
|
||||
path('delete/<int:pk>', delete_item, name="delete_item"),
|
||||
path('pricelevel/', index_pricelevel, name="index_pricelevel"),
|
||||
path('editpricelevel/<int:id>', add_pricelevel, name="edit_pricelevel"),
|
||||
path('addpricelevel/', add_pricelevel, name="add_pricelevel"),
|
||||
|
||||
]
|
||||
170
django/Item/views.py
Normal file
170
django/Item/views.py
Normal file
@ -0,0 +1,170 @@
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse
|
||||
from django.db import transaction
|
||||
from django.core.paginator import Paginator
|
||||
|
||||
from django.db.models import Q
|
||||
|
||||
from .models import Item, UOM
|
||||
from .forms import ItemForm, PriceLevelForm
|
||||
|
||||
from django.db.models import Q
|
||||
from Item.models import Item, PriceLevel, PriceLevelItem
|
||||
import json
|
||||
|
||||
def index_pricelevel(request):
|
||||
context={}
|
||||
context['objects']=PriceLevel.objects.order_by('Name')
|
||||
context['addurl'] = reverse('Item:add_pricelevel')
|
||||
# print(context['objects'])
|
||||
return render(request, "Item/index_pricelevel.html", context=context)
|
||||
|
||||
def add_pricelevel(request, id=None):
|
||||
context={}
|
||||
# print(f"idroot={id}")
|
||||
pricelevel1 = None
|
||||
plname=None
|
||||
context['pk'] = 'null'
|
||||
if id:
|
||||
print(f"edit id={id}")
|
||||
pricelevel1=get_object_or_404(PriceLevel, pk=id)
|
||||
plname= pricelevel1.Name
|
||||
print(plname)
|
||||
context['pk'] = pricelevel1.pk
|
||||
else:
|
||||
print("add new")
|
||||
|
||||
form = PriceLevelForm(request.POST or None, instance=pricelevel1)
|
||||
qs1 = Item.objects.filter(itempricelevel__PL__Name=plname).prefetch_related("itempricelevel").values('FullName','SalesPrice', 'itempricelevel__Price')
|
||||
qs2 = Item.objects.exclude(itempricelevel__PL__Name=plname).prefetch_related("itempricelevel").values( "FullName", "SalesPrice", "AlwaysNull")
|
||||
# print(qs2)
|
||||
qs = qs1.union(qs2)
|
||||
# print(type(qs))
|
||||
# print(qs)
|
||||
context['objects'] = qs
|
||||
context['form'] = form
|
||||
if request.POST:
|
||||
datas = request.POST.get('data')
|
||||
pk = request.POST.get('pk')
|
||||
print(pk)
|
||||
with transaction.atomic():
|
||||
if form.is_valid():
|
||||
print(form.cleaned_data["Name"])
|
||||
pricelevel = form.save()
|
||||
print("form pricelevel saved")
|
||||
# return redirect(reverse("index"))
|
||||
else:
|
||||
print("form not valid")
|
||||
return render(request, "Item/addedit_pricelevel.html", context=context)
|
||||
|
||||
# pricelevel = get_object_or_404(PriceLevel, pk=pk)
|
||||
|
||||
# print(f"pk={pk}; object={pricelevel}")
|
||||
# print(type(datas))
|
||||
# print(datas)
|
||||
datas = json.loads(datas)
|
||||
# print(type(datas))
|
||||
# print(datas)
|
||||
# print(form["Name"].value())
|
||||
blAllSaved = True
|
||||
if len(datas)>0:
|
||||
print("more than 0")
|
||||
for data in datas:
|
||||
print(type(data))
|
||||
print(data)
|
||||
# print(data['FullName'])
|
||||
if data['dtchange']=="1" or data['dtchange']=="2":
|
||||
## add new pricelevelitem
|
||||
print("dtchange 1,2")
|
||||
item = get_object_or_404(Item, FullName = f"{data['FullName']}")
|
||||
print(f"object=={item}")
|
||||
pricelevelitem, created = PriceLevelItem.objects.get_or_create(PL=pricelevel, IPL=item, defaults={'Price':data['price']})
|
||||
print(f"created={created}")
|
||||
print(f"object=={pricelevelitem}")
|
||||
if not created:
|
||||
pricelevelitem.Price = data['price']
|
||||
pricelevelitem.save()
|
||||
print(f"object=={pricelevelitem}")
|
||||
else:
|
||||
blAllSaved=False
|
||||
print("dtchange is NOT 1 or 2")
|
||||
if blAllSaved:
|
||||
return redirect(reverse('Item:index_pricelevel'))
|
||||
else:
|
||||
return redirect(reverse('Item:edit_pricelevel', pricelevel.pk ))
|
||||
else:
|
||||
return redirect(reverse('Item:index_pricelevel'))
|
||||
return render(request, "Item/addedit_pricelevel.html", context=context)
|
||||
|
||||
def edit_pricelevel(request, pk):
|
||||
context={}
|
||||
qs1 = Item.objects.filter(itempricelevel__PL__Name='B2020',).prefetch_related("itempricelevel").values('FullName','SalesPrice', 'itempricelevel__Price')
|
||||
qs2 = Item.objects.exclude(itempricelevel__PL__Name='B2020').prefetch_related("itempricelevel").values( "FullName", "SalesPrice", "AlwaysNull")
|
||||
# print(qs2)
|
||||
qs = qs1.union(qs2)
|
||||
# print(type(qs))
|
||||
# print(qs)
|
||||
context['objects'] = qs
|
||||
return render(request, "Item/addedit_pricelevel.html", context=context)
|
||||
|
||||
|
||||
|
||||
def index(request):
|
||||
context={}
|
||||
search = request.GET.get("q")
|
||||
items=Item.objects.all().order_by('FullName')
|
||||
if search:
|
||||
item = Item.objects.order_by('FullName').filter(Q(Name__icontains=search) | Q(FullName__icontains=search))
|
||||
else:
|
||||
item = Item.objects.order_by('FullName')
|
||||
paginator = Paginator(item, 25) # Show 25 contacts per page.
|
||||
page_number = request.GET.get("page")
|
||||
page_obj = paginator.get_page(page_number)
|
||||
# heads = [f.name for f in Item._meta.get_fields()]
|
||||
# print(heads)
|
||||
context['objects'] = page_obj
|
||||
context['items'] = items
|
||||
context['addurl'] = reverse('Item:add_item')
|
||||
return render(request, "Item/index.html", context=context)
|
||||
|
||||
|
||||
def add_item(request):
|
||||
form = ItemForm(request.POST or None)
|
||||
if request.POST:
|
||||
print(form["Name"].value())
|
||||
if form.is_valid():
|
||||
print(form.cleaned_data["Name"])
|
||||
form.save()
|
||||
return redirect(reverse("Item:index"))
|
||||
print(form["Name"].value())
|
||||
return render(request, "Item/addedit_item.html", {"form":form})
|
||||
|
||||
|
||||
def delete_item(request, pk):
|
||||
item = get_object_or_404(Item, pk=pk)
|
||||
item.delete()
|
||||
return HttpResponse(f"{pk} is deleted")
|
||||
|
||||
|
||||
def edit_item(request, pk):
|
||||
print(pk)
|
||||
print(request)
|
||||
item = get_object_or_404(Item, pk=pk)
|
||||
form = ItemForm( request.POST or None, instance=item)
|
||||
if request.method == "GET":
|
||||
print("GET")
|
||||
if item:
|
||||
# form = ItemForm(instance=item)
|
||||
return render(request, "Item/addedit_item.html", {"objects": [item,], "form":form})
|
||||
elif request.method == "POST":
|
||||
print("POST")
|
||||
# form = ItemForm(request.POST, instance=item)
|
||||
if form.is_valid():
|
||||
print("form is valid")
|
||||
item=form.save()
|
||||
# return redirect(reverse('Item:edit_item', kwargs={"pk":pk}))
|
||||
return render(request, "Item/index.html", {"objects": [item]})
|
||||
else:
|
||||
print("not valid form")
|
||||
return render(request, "Item/addedit_item.html", {"objects": [item,], "form":form})
|
||||
0
django/SalesOrder/__init__.py
Normal file
0
django/SalesOrder/__init__.py
Normal file
6
django/SalesOrder/admin.py
Normal file
6
django/SalesOrder/admin.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.contrib import admin
|
||||
from .models import SalesOrder, SalesOrderItemLine
|
||||
|
||||
|
||||
admin.site.register(SalesOrder)
|
||||
admin.site.register(SalesOrderItemLine)
|
||||
6
django/SalesOrder/apps.py
Normal file
6
django/SalesOrder/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SalesorderConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'SalesOrder'
|
||||
43
django/SalesOrder/forms.py
Normal file
43
django/SalesOrder/forms.py
Normal file
@ -0,0 +1,43 @@
|
||||
from django.forms import ModelForm
|
||||
from django import forms
|
||||
from .models import SalesOrder, SalesOrderItemLine
|
||||
from crispy_forms.helper import FormHelper
|
||||
|
||||
class SalesOrderForm(ModelForm):
|
||||
class Meta:
|
||||
model = SalesOrder
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
'TxnDate' : forms.DateInput(attrs={'type':"date"}),
|
||||
'BillAddr1' : forms.Textarea(attrs={'rows':7, 'style':'height:180px'}),
|
||||
'ShipAddr1' : forms.Textarea(attrs={'rows':7, 'style':'height:180px'}),
|
||||
'TotalAmount' : forms.TextInput(attrs={'class':'text-end hidden', 'onkeypress':'return event.preventDefault()'})
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SalesOrderForm, self).__init__(*args, **kwargs)
|
||||
self.helper = FormHelper(self)
|
||||
# self.helper.form_show_labels = False
|
||||
self.fields['TotalAmount'].label = False
|
||||
self.fields['TotalAmount'].field_class = ''
|
||||
|
||||
class SalesOrderItemLineForm(ModelForm):
|
||||
class Meta:
|
||||
model = SalesOrderItemLine
|
||||
fields = ('ItemRefFullName', 'Desc', 'Quantity', 'UnitOfMeasure', 'Rate', 'Amount', 'Invoiced', 'LineIsManuallyClosed')
|
||||
# fields = "__all__"
|
||||
widgets = {
|
||||
# 'ItemRefFullName' : forms.Select(choices=[('1', '1')]),
|
||||
'ItemRefFullName' : forms.NumberInput(attrs={'class':'hidden itemreffullname'}),
|
||||
'Desc' : forms.TextInput(attrs={'class':'desc'}),
|
||||
'Quantity' : forms.TextInput(attrs={'class':'quantity','onkeypress':'numberOnly(event)'}), #'onkeypress':'return (event.charCode >= 48 && event.charCode <= 57) || event.charCode == 46'}),
|
||||
'UnitOfMeasure' : forms.Select(attrs={'class':'unitofmeasure'}),
|
||||
'Rate' : forms.TextInput(attrs={'class':'rate text-end', 'onkeypress':'numberOnly(event)'}),
|
||||
'Amount' : forms.TextInput(attrs={'class':'amount text-end', 'onkeypress':'numberOnly(event)', 'onchange':'amountchanged(event)'}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SalesOrderItemLineForm, self).__init__(*args, **kwargs)
|
||||
self.helper = FormHelper(self)
|
||||
self.helper.form_show_labels = False
|
||||
# self.helper.form_class = "abc"
|
||||
# self.helper.field_class = "xyz"
|
||||
82
django/SalesOrder/migrations/0001_initial.py
Normal file
82
django/SalesOrder/migrations/0001_initial.py
Normal file
@ -0,0 +1,82 @@
|
||||
# Generated by Django 4.2 on 2023-05-13 18:32
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('Item', '0010_alter_pricelevelitem_price'),
|
||||
('Customer', '0005_alter_customer_coordinates_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SalesOrder',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('TxnDate', models.DateTimeField()),
|
||||
('RefNumber', models.CharField(max_length=30)),
|
||||
('BillAddr1', models.CharField(max_length=80)),
|
||||
('BillAddr2', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr3', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr4', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillAddr5', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillCity', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillState', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillPostalCode', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('BillCountry', models.CharField(default='Indonesia', max_length=80)),
|
||||
('BillNote', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr1', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr2', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr3', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr4', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipAddr5', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipCity', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipState', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipPostalCode', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipCountry', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('ShipNote', models.CharField(blank=True, max_length=200, null=True)),
|
||||
('PONumber', models.CharField(blank=True, max_length=30, null=True)),
|
||||
('ShipDate', models.DateTimeField(blank=True, null=True)),
|
||||
('DueDate', models.DateField(blank=True, null=True)),
|
||||
('Subtotal', models.DecimalField(decimal_places=2, max_digits=14)),
|
||||
('TotalAmount', models.DecimalField(decimal_places=2, max_digits=14)),
|
||||
('CustomerMsgRefFullName', models.CharField(blank=True, max_length=120, null=True)),
|
||||
('IsToBePrinted', models.BooleanField(default=False)),
|
||||
('IsToBeEmailed', models.BooleanField(default=False)),
|
||||
('IsManuallyClosed', models.BooleanField(default=False)),
|
||||
('IsFullyInvoiced', models.BooleanField(default=False)),
|
||||
('Memo', models.CharField(blank=True, max_length=120, null=True)),
|
||||
('NPWP', models.CharField(blank=True, max_length=20, null=True)),
|
||||
('KTP', models.CharField(blank=True, max_length=16, null=True)),
|
||||
('DMS_Cust_Name', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('DMS_Cust_Code', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Special_Cust', models.BooleanField(default=False)),
|
||||
('EFaktur_Name', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Efaktur_Address', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Coordinates', models.CharField(blank=True, max_length=30, null=True)),
|
||||
('TimeCreated', models.DateTimeField(auto_now_add=True)),
|
||||
('TimeModified', models.DateTimeField(auto_now=True)),
|
||||
('CustomerRefFullName', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='Customer.customer')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SalesOrderItemLine',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('Desc', models.CharField(blank=True, max_length=80, null=True)),
|
||||
('Quantity', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)),
|
||||
('Rate', models.DecimalField(blank=True, decimal_places=2, max_digits=11, null=True)),
|
||||
('Amount', models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True)),
|
||||
('Invoiced', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)),
|
||||
('LineIsManuallyClosed', models.BooleanField(default=False)),
|
||||
('CustomerRefFullName', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='SalesOrder.salesorder')),
|
||||
('ItemRefFullName', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='Item.item')),
|
||||
('UnitOfMeasure', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='Item.uom', verbose_name='UOM')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-14 18:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('SalesOrder', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='salesorder',
|
||||
name='TxnDate',
|
||||
field=models.DateField(),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2 on 2023-05-14 19:34
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('SalesOrder', '0002_alter_salesorder_txndate'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='salesorderitemline',
|
||||
old_name='CustomerRefFullName',
|
||||
new_name='SalesOrder',
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2 on 2023-05-15 08:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('SalesOrder', '0003_rename_customerreffullname_salesorderitemline_salesorder'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='salesorder',
|
||||
name='SalesRepRefFullName',
|
||||
field=models.CharField(blank=True, max_length=10, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='salesorder',
|
||||
name='TermsRefFullName',
|
||||
field=models.CharField(blank=True, max_length=10, null=True),
|
||||
),
|
||||
]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user