mirror of
https://github.com/bcomsugi/dasaproject.git
synced 2026-01-10 07:02:38 +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