Merge branch 'develop' into rename-doc-fix
This commit is contained in:
commit
000ac0068b
@ -63,6 +63,7 @@ install:
|
|||||||
- tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
- tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
||||||
- sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
- sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
||||||
- sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
- sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
||||||
|
- sudo apt-get install libcups2-dev
|
||||||
|
|
||||||
- cd ~/frappe-bench
|
- cd ~/frappe-bench
|
||||||
|
|
||||||
|
21
README.md
21
README.md
@ -13,9 +13,26 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Includes: Accounting, Inventory, Manufacturing, CRM, Sales, Purchase, Project Management, HRMS. Requires MariaDB.
|
ERPNext as a monolith includes the following areas for managing businesses:
|
||||||
|
|
||||||
ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript.
|
1. [Accounting](https://erpnext.com/docs/user/manual/en/accounts)
|
||||||
|
1. [Inventory](https://erpnext.com/docs/user/manual/en/stock)
|
||||||
|
1. [CRM](https://erpnext.com/docs/user/manual/en/CRM)
|
||||||
|
1. [Sales](https://erpnext.com/docs/user/manual/en/selling)
|
||||||
|
1. [Purchase](https://erpnext.com/docs/user/manual/en/buying)
|
||||||
|
1. [HRMS](https://erpnext.com/docs/user/manual/en/human-resources)
|
||||||
|
1. [Project Management](https://erpnext.com/docs/user/manual/en/projects)
|
||||||
|
1. [Support](https://erpnext.com/docs/user/manual/en/support)
|
||||||
|
1. [Asset Management](https://erpnext.com/docs/user/manual/en/asset)
|
||||||
|
1. [Quality Management](https://erpnext.com/docs/user/manual/en/quality-management)
|
||||||
|
1. [Manufacturing](https://erpnext.com/docs/user/manual/en/manufacturing)
|
||||||
|
1. [Website Management](https://erpnext.com/docs/user/manual/en/website)
|
||||||
|
1. [Customize ERPNext](https://erpnext.com/docs/user/manual/en/customize-erpnext)
|
||||||
|
1. [And More](https://erpnext.com/docs/user/manual/en/)
|
||||||
|
|
||||||
|
ERPNext requires MariaDB.
|
||||||
|
|
||||||
|
ERPNext is built on the [Frappe Framework](https://github.com/frappe/frappe), a full-stack web app framework built with Python & JavaScript.
|
||||||
|
|
||||||
- [User Guide](https://erpnext.com/docs/user)
|
- [User Guide](https://erpnext.com/docs/user)
|
||||||
- [Discussion Forum](https://discuss.erpnext.com/)
|
- [Discussion Forum](https://discuss.erpnext.com/)
|
||||||
|
@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '12.1.8'
|
__version__ = '12.2.0'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
@ -19,6 +19,11 @@ def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_d
|
|||||||
else:
|
else:
|
||||||
chart = frappe._dict(frappe.parse_json(chart))
|
chart = frappe._dict(frappe.parse_json(chart))
|
||||||
timespan = chart.timespan
|
timespan = chart.timespan
|
||||||
|
|
||||||
|
if chart.timespan == 'Select Date Range':
|
||||||
|
from_date = chart.from_date
|
||||||
|
to_date = chart.to_date
|
||||||
|
|
||||||
timegrain = chart.time_interval
|
timegrain = chart.time_interval
|
||||||
filters = frappe.parse_json(chart.filters_json)
|
filters = frappe.parse_json(chart.filters_json)
|
||||||
|
|
||||||
@ -88,7 +93,8 @@ def get_gl_entries(account, to_date):
|
|||||||
fields = ['posting_date', 'debit', 'credit'],
|
fields = ['posting_date', 'debit', 'credit'],
|
||||||
filters = [
|
filters = [
|
||||||
dict(posting_date = ('<', to_date)),
|
dict(posting_date = ('<', to_date)),
|
||||||
dict(account = ('in', child_accounts))
|
dict(account = ('in', child_accounts)),
|
||||||
|
dict(voucher_type = ('!=', 'Period Closing Voucher'))
|
||||||
],
|
],
|
||||||
order_by = 'posting_date asc')
|
order_by = 'posting_date asc')
|
||||||
|
|
||||||
|
@ -174,6 +174,8 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
|||||||
# GL Entry for crediting the amount in the deferred expense
|
# GL Entry for crediting the amount in the deferred expense
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
|
||||||
|
if amount == 0: return
|
||||||
|
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
doc.get_gl_dict({
|
doc.get_gl_dict({
|
||||||
|
@ -109,15 +109,16 @@ class Account(NestedSet):
|
|||||||
if not descendants: return
|
if not descendants: return
|
||||||
|
|
||||||
parent_acc_name_map = {}
|
parent_acc_name_map = {}
|
||||||
parent_acc_name = frappe.db.get_value('Account', self.parent_account, "account_name")
|
parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \
|
||||||
|
["account_name", "account_number"])
|
||||||
for d in frappe.db.get_values('Account',
|
for d in frappe.db.get_values('Account',
|
||||||
{"company": ["in", descendants], "account_name": parent_acc_name},
|
{ "company": ["in", descendants], "account_name": parent_acc_name,
|
||||||
|
"account_number": parent_acc_number },
|
||||||
["company", "name"], as_dict=True):
|
["company", "name"], as_dict=True):
|
||||||
parent_acc_name_map[d["company"]] = d["name"]
|
parent_acc_name_map[d["company"]] = d["name"]
|
||||||
|
|
||||||
if not parent_acc_name_map: return
|
if not parent_acc_name_map: return
|
||||||
|
|
||||||
self.create_account_for_child_company(parent_acc_name_map, descendants)
|
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
||||||
|
|
||||||
def validate_group_or_ledger(self):
|
def validate_group_or_ledger(self):
|
||||||
if self.get("__islocal"):
|
if self.get("__islocal"):
|
||||||
@ -159,7 +160,7 @@ class Account(NestedSet):
|
|||||||
if frappe.db.get_value("GL Entry", {"account": self.name}):
|
if frappe.db.get_value("GL Entry", {"account": self.name}):
|
||||||
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
|
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
|
||||||
|
|
||||||
def create_account_for_child_company(self, parent_acc_name_map, descendants):
|
def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
|
||||||
for company in descendants:
|
for company in descendants:
|
||||||
if not parent_acc_name_map.get(company):
|
if not parent_acc_name_map.get(company):
|
||||||
frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
|
frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
|
||||||
|
@ -1,465 +1,466 @@
|
|||||||
{
|
{
|
||||||
"country_code": "ae",
|
"country_code": "ae",
|
||||||
"name": "U.A.E - Chart of Accounts",
|
"name": "U.A.E - Chart of Accounts",
|
||||||
"tree": {
|
"tree": {
|
||||||
"Assets": {
|
"Assets": {
|
||||||
"Current Assets": {
|
"Current Assets": {
|
||||||
"Accounts Receivable": {
|
"Accounts Receivable": {
|
||||||
"Corporate Credit Cards": {
|
"Corporate Credit Cards": {
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Other Receivable": {
|
"Other Receivable": {
|
||||||
"Accrued Rebates Due from Suppliers": {
|
"Accrued Rebates Due from Suppliers": {
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Accrued Income from Suppliers": {
|
"Accrued Income from Suppliers": {
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Other Debtors": {
|
"Other Debtors": {
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Post Dated Cheques Received": {
|
"Post Dated Cheques Received": {
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Staff Receivable": {
|
"Staff Receivable": {
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Trade Receivable": {
|
"Trade Receivable": {
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Trade in Opening Fees": {
|
"Trade in Opening Fees": {
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"account_type": "Receivable"
|
"account_type": "Receivable"
|
||||||
},
|
},
|
||||||
"Cash in Hand & Banks": {
|
"Cash in Hand & Banks": {
|
||||||
"Banks": {
|
"Banks": {
|
||||||
"Bank Margin On LC & LG": {},
|
"Bank Margin On LC & LG": {},
|
||||||
"Banks Blocked Deposits": {},
|
"Banks Blocked Deposits": {},
|
||||||
"Banks Call Deposit Accounts": {},
|
"Banks Call Deposit Accounts": {},
|
||||||
"Banks Current Accounts": {
|
"Banks Current Accounts": {
|
||||||
"account_type": "Bank"
|
"account_type": "Bank"
|
||||||
},
|
},
|
||||||
"account_type": "Bank"
|
"account_type": "Bank"
|
||||||
},
|
},
|
||||||
"Cash in Hand": {
|
"Cash in Hand": {
|
||||||
"Cash in Safe": {
|
"Cash in Safe": {
|
||||||
"Main Safe": {
|
"Main Safe": {
|
||||||
"account_type": "Cash"
|
"account_type": "Cash"
|
||||||
},
|
},
|
||||||
"Main Safe - Foreign Currency": {
|
"Main Safe - Foreign Currency": {
|
||||||
"account_type": "Cash"
|
"account_type": "Cash"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Petty Cash": {
|
"Petty Cash": {
|
||||||
"Petty Cash - Administration": {
|
"Petty Cash - Administration": {
|
||||||
"account_type": "Cash"
|
"account_type": "Cash"
|
||||||
},
|
},
|
||||||
"Petty Cash - Others": {
|
"Petty Cash - Others": {
|
||||||
"account_type": "Cash"
|
"account_type": "Cash"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"account_type": "Cash"
|
"account_type": "Cash"
|
||||||
},
|
},
|
||||||
"Cash in Transit": {
|
"Cash in Transit": {
|
||||||
"Credit Cards": {
|
"Credit Cards": {
|
||||||
"Gateway Credit Cards": {
|
"Gateway Credit Cards": {
|
||||||
"account_type": "Bank"
|
"account_type": "Bank"
|
||||||
},
|
},
|
||||||
"Manual Visa & Master Cards": {
|
"Manual Visa & Master Cards": {
|
||||||
"account_type": "Bank"
|
"account_type": "Bank"
|
||||||
},
|
},
|
||||||
"PayPal Account": {
|
"PayPal Account": {
|
||||||
"account_type": "Bank"
|
"account_type": "Bank"
|
||||||
},
|
},
|
||||||
"Visa & Master Credit Cards": {
|
"Visa & Master Credit Cards": {
|
||||||
"account_type": "Bank"
|
"account_type": "Bank"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Inventory": {
|
"Inventory": {
|
||||||
"Consigned Stock": {
|
"Consigned Stock": {
|
||||||
"Handling Difference in Inventory": {
|
"Handling Difference in Inventory": {},
|
||||||
"account_type": "Stock Adjustment"
|
|
||||||
},
|
|
||||||
"Items Delivered to Customs on temporary Base": {}
|
"Items Delivered to Customs on temporary Base": {}
|
||||||
},
|
},
|
||||||
"Stock in Hand": {
|
"Stock in Hand": {
|
||||||
"account_type": "Stock"
|
"account_type": "Stock"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Preliminary and Preoperating Expenses": {
|
"Preliminary and Preoperating Expenses": {
|
||||||
"Preoperating Expenses": {}
|
"Preoperating Expenses": {}
|
||||||
},
|
},
|
||||||
"Prepayments & Deposits": {
|
"Prepayments & Deposits": {
|
||||||
"Deposits": {
|
"Deposits": {
|
||||||
"Deposit - Office Rent": {},
|
"Deposit - Office Rent": {},
|
||||||
"Deposit Others": {},
|
"Deposit Others": {},
|
||||||
"Deposit to Immigration (Visa)": {},
|
"Deposit to Immigration (Visa)": {},
|
||||||
"Deposits - Customs": {}
|
"Deposits - Customs": {}
|
||||||
},
|
},
|
||||||
"Prepaid Taxes": {
|
"Prepaid Taxes": {
|
||||||
"Sales Taxes Receivables": {},
|
"Sales Taxes Receivables": {},
|
||||||
"Withholding Tax Receivables": {}
|
"Withholding Tax Receivables": {}
|
||||||
},
|
},
|
||||||
"Prepayments": {
|
"Prepayments": {
|
||||||
"Other Prepayments": {},
|
"Other Prepayments": {},
|
||||||
"PrePaid Advertisement Expenses": {},
|
"PrePaid Advertisement Expenses": {},
|
||||||
"Prepaid Bank Guarantee": {},
|
"Prepaid Bank Guarantee": {},
|
||||||
"Prepaid Consultancy Fees": {},
|
"Prepaid Consultancy Fees": {},
|
||||||
"Prepaid Employees Housing": {},
|
"Prepaid Employees Housing": {},
|
||||||
"Prepaid Finance charge for Loans": {},
|
"Prepaid Finance charge for Loans": {},
|
||||||
"Prepaid Legal Fees": {},
|
"Prepaid Legal Fees": {},
|
||||||
"Prepaid License Fees": {},
|
"Prepaid License Fees": {},
|
||||||
"Prepaid Life Insurance": {},
|
"Prepaid Life Insurance": {},
|
||||||
"Prepaid Maintenance": {},
|
"Prepaid Maintenance": {},
|
||||||
"Prepaid Medical Insurance": {},
|
"Prepaid Medical Insurance": {},
|
||||||
"Prepaid Office Rent": {},
|
"Prepaid Office Rent": {},
|
||||||
"Prepaid Other Insurance": {},
|
"Prepaid Other Insurance": {},
|
||||||
"Prepaid Schooling Fees": {},
|
"Prepaid Schooling Fees": {},
|
||||||
"Prepaid Site Hosting Fees": {},
|
"Prepaid Site Hosting Fees": {},
|
||||||
"Prepaid Sponsorship Fees": {}
|
"Prepaid Sponsorship Fees": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Long Term Assets": {
|
"Long Term Assets": {
|
||||||
"Fixed Assets": {
|
"Fixed Assets": {
|
||||||
"Accumulated Depreciation": {
|
"Accumulated Depreciation": {
|
||||||
"Acc. Depreciation of Motor Vehicles": {
|
"Acc. Depreciation of Motor Vehicles": {
|
||||||
"account_type": "Accumulated Depreciation"
|
"account_type": "Accumulated Depreciation"
|
||||||
},
|
},
|
||||||
"Acc. Deprn.Computer Hardware & Software": {
|
"Acc. Deprn.Computer Hardware & Software": {
|
||||||
"account_type": "Accumulated Depreciation"
|
"account_type": "Accumulated Depreciation"
|
||||||
},
|
},
|
||||||
"Acc.Deprn.of Furniture & Office Equipment": {
|
"Acc.Deprn.of Furniture & Office Equipment": {
|
||||||
"account_type": "Accumulated Depreciation"
|
"account_type": "Accumulated Depreciation"
|
||||||
},
|
},
|
||||||
"Amortisation on Leasehold Improvement": {
|
"Amortisation on Leasehold Improvement": {
|
||||||
"account_type": "Accumulated Depreciation"
|
"account_type": "Accumulated Depreciation"
|
||||||
},
|
},
|
||||||
"account_type": "Accumulated Depreciation"
|
"account_type": "Accumulated Depreciation"
|
||||||
},
|
},
|
||||||
"Fixed Assets (Cost Price)": {
|
"Fixed Assets (Cost Price)": {
|
||||||
"Computer Hardware & Software": {
|
"Computer Hardware & Software": {
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Furniture and Equipment": {
|
"Furniture and Equipment": {
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Leasehold Improvement": {},
|
"Leasehold Improvement": {},
|
||||||
"Motor Vehicles": {
|
"Motor Vehicles": {
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
},
|
},
|
||||||
"Work In Progress": {},
|
"Work In Progress": {},
|
||||||
"account_type": "Fixed Asset"
|
"account_type": "Fixed Asset"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Intangible Assets": {
|
"Intangible Assets": {
|
||||||
"Computer Card Renewal": {},
|
"Computer Card Renewal": {},
|
||||||
"Disposal of Outlets": {},
|
"Disposal of Outlets": {},
|
||||||
"Registration of Trademarks": {}
|
"Registration of Trademarks": {}
|
||||||
},
|
},
|
||||||
"Intercompany Accounts": {},
|
"Intercompany Accounts": {},
|
||||||
"Investments": {
|
"Investments": {
|
||||||
"Investments in Subsidiaries": {}
|
"Investments in Subsidiaries": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root_type": "Asset"
|
"root_type": "Asset"
|
||||||
},
|
},
|
||||||
"Closing And Temporary Accounts": {
|
"Closing And Temporary Accounts": {
|
||||||
"Closing Accounts": {
|
"Closing Accounts": {
|
||||||
"Closing Account": {}
|
"Closing Account": {}
|
||||||
},
|
},
|
||||||
"root_type": "Liability"
|
"root_type": "Liability"
|
||||||
},
|
},
|
||||||
"Expenses": {
|
"Expenses": {
|
||||||
"Commercial Expenses": {
|
"Commercial Expenses": {
|
||||||
"Consultancy Fees": {},
|
"Consultancy Fees": {},
|
||||||
"Provision for Doubtful Debts": {}
|
"Provision for Doubtful Debts": {}
|
||||||
},
|
},
|
||||||
"Cost of Sale": {
|
"Cost of Sale": {
|
||||||
"Cost Of Goods Sold": {
|
"Cost Of Goods Sold": {
|
||||||
"Cost Of Goods Sold I/C Sales": {},
|
"Cost Of Goods Sold I/C Sales": {},
|
||||||
"Cost of Goods Sold in Trading": {
|
"Cost of Goods Sold in Trading": {
|
||||||
"account_type": "Cost of Goods Sold"
|
"account_type": "Cost of Goods Sold"
|
||||||
},
|
},
|
||||||
"account_type": "Cost of Goods Sold"
|
"account_type": "Cost of Goods Sold"
|
||||||
},
|
},
|
||||||
"Expenses Included In Valuation": {
|
"Expenses Included In Valuation": {
|
||||||
"account_type": "Expenses Included In Valuation"
|
"account_type": "Expenses Included In Valuation"
|
||||||
|
},
|
||||||
|
"Stock Adjustment": {
|
||||||
|
"account_type": "Stock Adjustment"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Depreciation": {
|
"Depreciation": {
|
||||||
"Depreciation & Amortization": {
|
"Depreciation & Amortization": {
|
||||||
"Amortization on Leasehold Improvement": {},
|
"Amortization on Leasehold Improvement": {},
|
||||||
"Depreciation Of Computer Hard & Soft": {
|
"Depreciation Of Computer Hard & Soft": {
|
||||||
"account_type": "Depreciation"
|
"account_type": "Depreciation"
|
||||||
},
|
},
|
||||||
"Depreciation Of Furniture & Office Equipment\n\t\t\t": {
|
"Depreciation Of Furniture & Office Equipment\n\t\t\t": {
|
||||||
"account_type": "Depreciation"
|
"account_type": "Depreciation"
|
||||||
},
|
},
|
||||||
"Depreciation Of Motor Vehicles": {
|
"Depreciation Of Motor Vehicles": {
|
||||||
"account_type": "Depreciation"
|
"account_type": "Depreciation"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Direct Expenses": {
|
"Direct Expenses": {
|
||||||
"Financial Charges": {
|
"Financial Charges": {
|
||||||
"Air Miles Card Charges": {},
|
"Air Miles Card Charges": {},
|
||||||
"Amex Credit Cards Charges": {},
|
"Amex Credit Cards Charges": {},
|
||||||
"Bank Finance & Loan Charges": {},
|
"Bank Finance & Loan Charges": {},
|
||||||
"Credit Card Charges": {},
|
"Credit Card Charges": {},
|
||||||
"Credit Card Swipe Charges": {},
|
"Credit Card Swipe Charges": {},
|
||||||
"PayPal Charges": {}
|
"PayPal Charges": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"MISC Charges": {
|
"MISC Charges": {
|
||||||
"Other Charges": {
|
"Other Charges": {
|
||||||
"Capital Loss": {
|
"Capital Loss": {
|
||||||
"Disposal of Business Branch": {},
|
"Disposal of Business Branch": {},
|
||||||
"Loss On Fixed Assets Disposal": {},
|
"Loss On Fixed Assets Disposal": {},
|
||||||
"Loss on Difference on Exchange": {}
|
"Loss on Difference on Exchange": {}
|
||||||
},
|
},
|
||||||
"Other Non Operating Exp": {
|
"Other Non Operating Exp": {
|
||||||
"Other Non Operating Expenses": {}
|
"Other Non Operating Expenses": {}
|
||||||
},
|
},
|
||||||
"Previous Year Adjustments": {
|
"Previous Year Adjustments": {
|
||||||
"Previous Year Adjustments Account": {}
|
"Previous Year Adjustments Account": {}
|
||||||
},
|
},
|
||||||
"Royalty Fees": {
|
"Royalty Fees": {
|
||||||
"Royalty to Parent Co.": {}
|
"Royalty to Parent Co.": {}
|
||||||
},
|
},
|
||||||
"Tax / Zakat Expenses": {
|
"Tax / Zakat Expenses": {
|
||||||
"Income Tax": {
|
"Income Tax": {
|
||||||
"account_type": "Tax"
|
"account_type": "Tax"
|
||||||
},
|
},
|
||||||
"Zakat": {},
|
"Zakat": {},
|
||||||
"account_type": "Tax"
|
"account_type": "Tax"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Share Resources": {
|
"Share Resources": {
|
||||||
"Share Resource Expenses Account": {}
|
"Share Resource Expenses Account": {}
|
||||||
},
|
},
|
||||||
"Store Operating Expenses": {
|
"Store Operating Expenses": {
|
||||||
"Selling, General & Admin Expenses": {
|
"Selling, General & Admin Expenses": {
|
||||||
"Advertising Expenses": {
|
"Advertising Expenses": {
|
||||||
"Other - Advertising Expenses": {}
|
"Other - Advertising Expenses": {}
|
||||||
},
|
},
|
||||||
"Bank & Finance Charges": {
|
"Bank & Finance Charges": {
|
||||||
"Other Bank Charges": {}
|
"Other Bank Charges": {}
|
||||||
},
|
},
|
||||||
"Communications": {
|
"Communications": {
|
||||||
"Courier": {},
|
"Courier": {},
|
||||||
"Others - Communication": {},
|
"Others - Communication": {},
|
||||||
"Telephone": {},
|
"Telephone": {},
|
||||||
"Web Site Hosting Fees": {}
|
"Web Site Hosting Fees": {}
|
||||||
},
|
},
|
||||||
"Office & Various Expenses": {
|
"Office & Various Expenses": {
|
||||||
"Cleaning": {},
|
"Cleaning": {},
|
||||||
"Conveyance Expenses": {},
|
"Conveyance Expenses": {},
|
||||||
"Gifts & Donations": {},
|
"Gifts & Donations": {},
|
||||||
"Insurance": {},
|
"Insurance": {},
|
||||||
"Kitchen and Buffet Expenses": {},
|
"Kitchen and Buffet Expenses": {},
|
||||||
"Maintenance": {},
|
"Maintenance": {},
|
||||||
"Others - Office Various Expenses": {},
|
"Others - Office Various Expenses": {},
|
||||||
"Security & Guard": {},
|
"Security & Guard": {},
|
||||||
"Stationary From Suppliers": {},
|
"Stationary From Suppliers": {},
|
||||||
"Stationary Out Of Stock": {},
|
"Stationary Out Of Stock": {},
|
||||||
"Subscriptions": {},
|
"Subscriptions": {},
|
||||||
"Training": {},
|
"Training": {},
|
||||||
"Vehicle Expenses": {}
|
"Vehicle Expenses": {}
|
||||||
},
|
},
|
||||||
"Personnel Cost": {
|
"Personnel Cost": {
|
||||||
"Basic Salary": {},
|
"Basic Salary": {},
|
||||||
"End Of Service Indemnity": {},
|
"End Of Service Indemnity": {},
|
||||||
"Housing Allowance": {},
|
"Housing Allowance": {},
|
||||||
"Leave Salary": {},
|
"Leave Salary": {},
|
||||||
"Leave Ticket": {},
|
"Leave Ticket": {},
|
||||||
"Life Insurance": {},
|
"Life Insurance": {},
|
||||||
"Medical Insurance": {},
|
"Medical Insurance": {},
|
||||||
"Personnel Cost Others": {},
|
"Personnel Cost Others": {},
|
||||||
"Sales Commission": {},
|
"Sales Commission": {},
|
||||||
"Staff School Allowances": {},
|
"Staff School Allowances": {},
|
||||||
"Transportation Allowance": {},
|
"Transportation Allowance": {},
|
||||||
"Uniform": {},
|
"Uniform": {},
|
||||||
"Visa Expenses": {}
|
"Visa Expenses": {}
|
||||||
},
|
},
|
||||||
"Professional & Legal Fees": {
|
"Professional & Legal Fees": {
|
||||||
"Audit Fees": {},
|
"Audit Fees": {},
|
||||||
"Legal fees": {},
|
"Legal fees": {},
|
||||||
"Others - Professional Fees": {},
|
"Others - Professional Fees": {},
|
||||||
"Sponsorship Fees": {},
|
"Sponsorship Fees": {},
|
||||||
"Trade License Fees": {}
|
"Trade License Fees": {}
|
||||||
},
|
},
|
||||||
"Provision & Write Off": {
|
"Provision & Write Off": {
|
||||||
"Amortisation of Preoperating Expenses": {},
|
"Amortisation of Preoperating Expenses": {},
|
||||||
"Cash Shortage": {},
|
"Cash Shortage": {},
|
||||||
"Others - Provision & Write off": {},
|
"Others - Provision & Write off": {},
|
||||||
"Write Off Inventory": {},
|
"Write Off Inventory": {},
|
||||||
"Write Off Receivables & Payables": {}
|
"Write Off Receivables & Payables": {}
|
||||||
},
|
},
|
||||||
"Rent Expenses": {
|
"Rent Expenses": {
|
||||||
"Office Rent": {},
|
"Office Rent": {},
|
||||||
"Warehouse Rent": {}
|
"Warehouse Rent": {}
|
||||||
},
|
},
|
||||||
"Travel Expenses": {
|
"Travel Expenses": {
|
||||||
"Air tickets": {},
|
"Air tickets": {},
|
||||||
"Hotel": {},
|
"Hotel": {},
|
||||||
"Meals": {},
|
"Meals": {},
|
||||||
"Others": {},
|
"Others": {},
|
||||||
"Per Diem": {}
|
"Per Diem": {}
|
||||||
},
|
},
|
||||||
"Utilities": {
|
"Utilities": {
|
||||||
"Other Utility Cahrges": {},
|
"Other Utility Cahrges": {},
|
||||||
"Water & Electricity": {}
|
"Water & Electricity": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root_type": "Expense"
|
"root_type": "Expense"
|
||||||
},
|
},
|
||||||
"Liabilities": {
|
"Liabilities": {
|
||||||
"Current Liabilities": {
|
"Current Liabilities": {
|
||||||
"Accounts Payable": {
|
"Accounts Payable": {
|
||||||
"Payables": {
|
"Payables": {
|
||||||
"Advance Payable to Suppliers": {
|
"Advance Payable to Suppliers": {
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"Consigned Payable": {
|
"Consigned Payable": {
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"Other Payable": {
|
"Other Payable": {
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"Post Dated Cheques Paid": {
|
"Post Dated Cheques Paid": {
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"Staff Payable": {},
|
"Staff Payable": {},
|
||||||
"Suppliers Price Protection": {
|
"Suppliers Price Protection": {
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"Trade Payable": {
|
"Trade Payable": {
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
},
|
},
|
||||||
"account_type": "Payable"
|
"account_type": "Payable"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Accruals & Provisions": {
|
"Accruals & Provisions": {
|
||||||
"Accruals": {
|
"Accruals": {
|
||||||
"Accrued Personnel Cost": {
|
"Accrued Personnel Cost": {
|
||||||
"Accrued - Commissions": {},
|
"Accrued - Commissions": {},
|
||||||
"Accrued - Leave Salary": {},
|
"Accrued - Leave Salary": {},
|
||||||
"Accrued - Leave Tickets": {},
|
"Accrued - Leave Tickets": {},
|
||||||
"Accrued - Salaries": {},
|
"Accrued - Salaries": {},
|
||||||
"Accrued Other Personnel Cost": {},
|
"Accrued Other Personnel Cost": {},
|
||||||
"Accrued Salaries Increment": {},
|
"Accrued Salaries Increment": {},
|
||||||
"Accrued-Staff Bonus": {}
|
"Accrued-Staff Bonus": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Accrued Expenses": {
|
"Accrued Expenses": {
|
||||||
"Accrued Other Expenses": {
|
"Accrued Other Expenses": {
|
||||||
"Accrued - Audit Fees": {},
|
"Accrued - Audit Fees": {},
|
||||||
"Accrued - Office Rent": {},
|
"Accrued - Office Rent": {},
|
||||||
"Accrued - Sponsorship": {},
|
"Accrued - Sponsorship": {},
|
||||||
"Accrued - Telephone": {},
|
"Accrued - Telephone": {},
|
||||||
"Accrued - Utilities": {},
|
"Accrued - Utilities": {},
|
||||||
"Accrued Others": {}
|
"Accrued Others": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Other Current Liabilities": {
|
"Other Current Liabilities": {
|
||||||
"Accrued Dubai Customs": {},
|
"Accrued Dubai Customs": {},
|
||||||
"Deferred income": {},
|
"Deferred income": {},
|
||||||
"Shipping & Handling": {}
|
"Shipping & Handling": {}
|
||||||
},
|
},
|
||||||
"Provisions": {
|
"Provisions": {
|
||||||
"Tax Payables": {
|
"Tax Payables": {
|
||||||
"Income Tax Payable": {},
|
"Income Tax Payable": {},
|
||||||
"Sales Tax Payable": {},
|
"Sales Tax Payable": {},
|
||||||
"Withholding Tax Payable": {}
|
"Withholding Tax Payable": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Short Term Loan": {}
|
"Short Term Loan": {}
|
||||||
},
|
},
|
||||||
"Duties and Taxes": {
|
"Duties and Taxes": {
|
||||||
"account_type": "Tax",
|
"account_type": "Tax",
|
||||||
"is_group": 1
|
"is_group": 1
|
||||||
},
|
},
|
||||||
"Reservations & Credit Notes": {
|
"Reservations & Credit Notes": {
|
||||||
"Credit Notes": {
|
"Credit Notes": {
|
||||||
"Credit Notes to Customers": {},
|
"Credit Notes to Customers": {},
|
||||||
"Reservations": {}
|
"Reservations": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Stock Liabilities": {
|
"Stock Liabilities": {
|
||||||
"Stock Received But Not Billed": {
|
"Stock Received But Not Billed": {
|
||||||
"account_type": "Stock Received But Not Billed"
|
"account_type": "Stock Received But Not Billed"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Unearned Income": {}
|
"Unearned Income": {}
|
||||||
},
|
},
|
||||||
"Long Term Liabilities": {
|
"Long Term Liabilities": {
|
||||||
"Long Term Loans & Provisions": {}
|
"Long Term Loans & Provisions": {}
|
||||||
},
|
},
|
||||||
"root_type": "Liability"
|
"root_type": "Liability"
|
||||||
},
|
},
|
||||||
"Revenue": {
|
"Revenue": {
|
||||||
"Direct Revenue": {
|
"Direct Revenue": {
|
||||||
"Other Direct Revenue": {
|
"Other Direct Revenue": {
|
||||||
"Other Revenue - Operating": {
|
"Other Revenue - Operating": {
|
||||||
"Advertising Income": {},
|
"Advertising Income": {},
|
||||||
"Branding Income": {},
|
"Branding Income": {},
|
||||||
"Early Setmt Margin from Suppliers": {},
|
"Early Setmt Margin from Suppliers": {},
|
||||||
"Marketing Rebate from Suppliers": {},
|
"Marketing Rebate from Suppliers": {},
|
||||||
"Rebate from Suppliers": {},
|
"Rebate from Suppliers": {},
|
||||||
"Service Income": {},
|
"Service Income": {},
|
||||||
"Space Rental Income": {}
|
"Space Rental Income": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Indirect Revenue": {
|
"Indirect Revenue": {
|
||||||
"Other Indirect Revenue": {
|
"Other Indirect Revenue": {
|
||||||
"Capital Gain": {},
|
"Capital Gain": {},
|
||||||
"Excess In Till": {},
|
"Excess In Till": {},
|
||||||
"Gain On Difference Of Exchange": {},
|
"Gain On Difference Of Exchange": {},
|
||||||
"Management Consultancy Fees": {},
|
"Management Consultancy Fees": {},
|
||||||
"Other Income": {}
|
"Other Income": {}
|
||||||
},
|
},
|
||||||
"Other Revenue - Non Operating": {
|
"Other Revenue - Non Operating": {
|
||||||
"Interest Revenue": {},
|
"Interest Revenue": {},
|
||||||
"Interest from FD": {},
|
"Interest from FD": {},
|
||||||
"Products Listing Fees from Suppliers": {},
|
"Products Listing Fees from Suppliers": {},
|
||||||
"Trade Opening Fees from suppliers": {}
|
"Trade Opening Fees from suppliers": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Sales": {
|
"Sales": {
|
||||||
"Sales from Other Regions": {
|
"Sales from Other Regions": {
|
||||||
"Sales from Other Region": {}
|
"Sales from Other Region": {}
|
||||||
},
|
},
|
||||||
"Sales of same region": {
|
"Sales of same region": {
|
||||||
"Management Consultancy Fees 1": {},
|
"Management Consultancy Fees 1": {},
|
||||||
"Sales Account": {},
|
"Sales Account": {},
|
||||||
"Sales of I/C": {}
|
"Sales of I/C": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root_type": "Income"
|
"root_type": "Income"
|
||||||
},
|
},
|
||||||
"Share Holder Equity": {
|
"Share Holder Equity": {
|
||||||
"Capital": {
|
"Capital": {
|
||||||
"Contributed Capital": {},
|
"Contributed Capital": {},
|
||||||
"Share Capital": {},
|
"Share Capital": {},
|
||||||
"Shareholders Current A/c": {},
|
"Shareholders Current A/c": {},
|
||||||
"Sub Ordinated Loan": {},
|
"Sub Ordinated Loan": {},
|
||||||
"Treasury Stocks": {}
|
"Treasury Stocks": {}
|
||||||
},
|
},
|
||||||
"Retained Earnings": {
|
"Retained Earnings": {
|
||||||
"Current Year Results": {},
|
"Current Year Results": {},
|
||||||
"Dividends Paid": {},
|
"Dividends Paid": {},
|
||||||
"Previous Years Results": {}
|
"Previous Years Results": {}
|
||||||
},
|
},
|
||||||
"account_type": "Equity",
|
"account_type": "Equity",
|
||||||
"root_type": "Equity"
|
"root_type": "Equity"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ def _make_test_records(verbose):
|
|||||||
["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"]
|
["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"]
|
||||||
]
|
]
|
||||||
|
|
||||||
for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"]]:
|
for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"], ["_Test Company with perpetual inventory", "TCP1"]]:
|
||||||
test_objects = make_test_objects("Account", [{
|
test_objects = make_test_objects("Account", [{
|
||||||
"doctype": "Account",
|
"doctype": "Account",
|
||||||
"account_name": account_name,
|
"account_name": account_name,
|
||||||
|
@ -24,6 +24,11 @@ class AccountingDimension(Document):
|
|||||||
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
|
||||||
frappe.throw(msg)
|
frappe.throw(msg)
|
||||||
|
|
||||||
|
exists = frappe.db.get_value("Accounting Dimension", {'document_type': self.document_type}, ['name'])
|
||||||
|
|
||||||
|
if exists and self.is_new():
|
||||||
|
frappe.throw("Document Type already used as a dimension")
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
if frappe.flags.in_test:
|
if frappe.flags.in_test:
|
||||||
make_dimension_in_accounting_doctypes(doc=self)
|
make_dimension_in_accounting_doctypes(doc=self)
|
||||||
@ -60,7 +65,8 @@ def make_dimension_in_accounting_doctypes(doc):
|
|||||||
"label": doc.label,
|
"label": doc.label,
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": doc.document_type,
|
"options": doc.document_type,
|
||||||
"insert_after": insert_after_field
|
"insert_after": insert_after_field,
|
||||||
|
"owner": "Administrator"
|
||||||
}
|
}
|
||||||
|
|
||||||
if doctype == "Budget":
|
if doctype == "Budget":
|
||||||
|
@ -41,8 +41,8 @@ class AccountingPeriod(Document):
|
|||||||
|
|
||||||
def get_doctypes_for_closing(self):
|
def get_doctypes_for_closing(self):
|
||||||
docs_for_closing = []
|
docs_for_closing = []
|
||||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation",
|
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
|
||||||
"Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
|
"Bank Reconciliation", "Asset", "Stock Entry"]
|
||||||
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
||||||
for closed_doctype in closed_doctypes:
|
for closed_doctype in closed_doctypes:
|
||||||
docs_for_closing.append(closed_doctype)
|
docs_for_closing.append(closed_doctype)
|
||||||
|
@ -15,8 +15,8 @@ class AccountsSettings(Document):
|
|||||||
frappe.clear_cache()
|
frappe.clear_cache()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
for f in ["add_taxes_from_item_tax_template"]:
|
frappe.db.set_default("add_taxes_from_item_tax_template",
|
||||||
frappe.db.set_default(f, self.get(f, ""))
|
self.get("add_taxes_from_item_tax_template", 0))
|
||||||
|
|
||||||
self.validate_stale_days()
|
self.validate_stale_days()
|
||||||
self.enable_payment_schedule_in_print()
|
self.enable_payment_schedule_in_print()
|
||||||
|
@ -15,8 +15,8 @@ def upload_bank_statement():
|
|||||||
with open(frappe.uploaded_file, "rb") as upfile:
|
with open(frappe.uploaded_file, "rb") as upfile:
|
||||||
fcontent = upfile.read()
|
fcontent = upfile.read()
|
||||||
else:
|
else:
|
||||||
from frappe.utils.file_manager import get_uploaded_content
|
fcontent = frappe.local.uploaded_file
|
||||||
fname, fcontent = get_uploaded_content()
|
fname = frappe.local.uploaded_filename
|
||||||
|
|
||||||
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
|
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
|
||||||
from frappe.utils.csvutils import read_csv_content
|
from frappe.utils.csvutils import read_csv_content
|
||||||
|
@ -185,7 +185,8 @@ def validate_account_types(accounts):
|
|||||||
return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing))
|
return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing))
|
||||||
|
|
||||||
account_types_for_group = ["Bank", "Cash", "Stock"]
|
account_types_for_group = ["Bank", "Cash", "Stock"]
|
||||||
account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] not in ('', 1)]
|
# fix logic bug
|
||||||
|
account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] == 1]
|
||||||
|
|
||||||
missing = list(set(account_types_for_group) - set(account_groups))
|
missing = list(set(account_types_for_group) - set(account_groups))
|
||||||
if missing:
|
if missing:
|
||||||
|
@ -18,6 +18,7 @@ class CostCenter(NestedSet):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
|
self.validate_parent_cost_center()
|
||||||
|
|
||||||
def validate_mandatory(self):
|
def validate_mandatory(self):
|
||||||
if self.cost_center_name != self.company and not self.parent_cost_center:
|
if self.cost_center_name != self.company and not self.parent_cost_center:
|
||||||
@ -25,6 +26,12 @@ class CostCenter(NestedSet):
|
|||||||
elif self.cost_center_name == self.company and self.parent_cost_center:
|
elif self.cost_center_name == self.company and self.parent_cost_center:
|
||||||
frappe.throw(_("Root cannot have a parent cost center"))
|
frappe.throw(_("Root cannot have a parent cost center"))
|
||||||
|
|
||||||
|
def validate_parent_cost_center(self):
|
||||||
|
if self.parent_cost_center:
|
||||||
|
if not frappe.db.get_value('Cost Center', self.parent_cost_center, 'is_group'):
|
||||||
|
frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format(
|
||||||
|
frappe.bold(self.parent_cost_center)))
|
||||||
|
|
||||||
def convert_group_to_ledger(self):
|
def convert_group_to_ledger(self):
|
||||||
if self.check_if_child_exists():
|
if self.check_if_child_exists():
|
||||||
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))
|
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Cost Center')
|
test_records = frappe.get_test_records('Cost Center')
|
||||||
|
|
||||||
|
class TestCostCenter(unittest.TestCase):
|
||||||
|
def test_cost_center_creation_against_child_node(self):
|
||||||
|
|
||||||
|
if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}):
|
||||||
|
frappe.get_doc(test_records[1]).insert()
|
||||||
|
|
||||||
|
cost_center = frappe.get_doc({
|
||||||
|
'doctype': 'Cost Center',
|
||||||
|
'cost_center_name': '_Test Cost Center 3',
|
||||||
|
'parent_cost_center': '_Test Cost Center 2 - _TC',
|
||||||
|
'is_group': 0,
|
||||||
|
'company': '_Test Company'
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, cost_center.save)
|
||||||
|
|
||||||
def create_cost_center(**args):
|
def create_cost_center(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
@ -2,6 +2,15 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Coupon Code', {
|
frappe.ui.form.on('Coupon Code', {
|
||||||
|
setup: function(frm) {
|
||||||
|
frm.set_query("pricing_rule", function() {
|
||||||
|
return {
|
||||||
|
filters: [
|
||||||
|
["Pricing Rule","coupon_code_based", "=", "1"]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
coupon_name:function(frm){
|
coupon_name:function(frm){
|
||||||
if (frm.doc.__islocal===1) {
|
if (frm.doc.__islocal===1) {
|
||||||
frm.trigger("make_coupon_code");
|
frm.trigger("make_coupon_code");
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"description": "e.g. \"Summer Holiday 2019 Offer 20\"",
|
||||||
"fieldname": "coupon_name",
|
"fieldname": "coupon_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Coupon Name",
|
"label": "Coupon Name",
|
||||||
@ -50,7 +51,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "To be used to get discount",
|
"description": "unique e.g. SAVE20 To be used to get discount",
|
||||||
"fieldname": "coupon_code",
|
"fieldname": "coupon_code",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Coupon Code",
|
"label": "Coupon Code",
|
||||||
@ -62,12 +63,13 @@
|
|||||||
"fieldname": "pricing_rule",
|
"fieldname": "pricing_rule",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Pricing Rule",
|
"label": "Pricing Rule",
|
||||||
"options": "Pricing Rule"
|
"options": "Pricing Rule",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "uses",
|
"fieldname": "uses",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Uses"
|
"label": "Validity and Usage"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "valid_from",
|
"fieldname": "valid_from",
|
||||||
@ -113,7 +115,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-10-15 14:12:22.686986",
|
"modified": "2019-10-19 14:48:14.602481",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Coupon Code",
|
"name": "Coupon Code",
|
||||||
|
@ -29,7 +29,6 @@ class GLEntry(Document):
|
|||||||
self.validate_and_set_fiscal_year()
|
self.validate_and_set_fiscal_year()
|
||||||
self.pl_must_have_cost_center()
|
self.pl_must_have_cost_center()
|
||||||
self.validate_cost_center()
|
self.validate_cost_center()
|
||||||
self.validate_dimensions_for_pl_and_bs()
|
|
||||||
|
|
||||||
if not self.flags.from_repost:
|
if not self.flags.from_repost:
|
||||||
self.check_pl_account()
|
self.check_pl_account()
|
||||||
@ -39,6 +38,7 @@ class GLEntry(Document):
|
|||||||
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
|
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
|
||||||
if not from_repost:
|
if not from_repost:
|
||||||
self.validate_account_details(adv_adj)
|
self.validate_account_details(adv_adj)
|
||||||
|
self.validate_dimensions_for_pl_and_bs()
|
||||||
check_freezing_date(self.posting_date, adv_adj)
|
check_freezing_date(self.posting_date, adv_adj)
|
||||||
|
|
||||||
validate_frozen_account(self.account, adv_adj)
|
validate_frozen_account(self.account, adv_adj)
|
||||||
|
@ -190,7 +190,6 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
|||||||
if(jvd.reference_type==="Employee Advance") {
|
if(jvd.reference_type==="Employee Advance") {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'status': ['=', 'Unpaid'],
|
|
||||||
'docstatus': 1
|
'docstatus': 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -398,7 +397,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
|||||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
|
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
|
||||||
args: {
|
args: {
|
||||||
"account_type": (doc.voucher_type=="Bank Entry" ?
|
"account_type": (doc.voucher_type=="Bank Entry" ?
|
||||||
"Bank" : (doc.voucher_type=="Cash" ? "Cash" : null)),
|
"Bank" : (doc.voucher_type=="Cash Entry" ? "Cash" : null)),
|
||||||
"company": doc.company
|
"company": doc.company
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
@ -570,7 +569,7 @@ $.extend(erpnext.journal_entry, {
|
|||||||
},
|
},
|
||||||
{fieldtype: "Date", fieldname: "posting_date", label: __("Date"), reqd: 1,
|
{fieldtype: "Date", fieldname: "posting_date", label: __("Date"), reqd: 1,
|
||||||
default: frm.doc.posting_date},
|
default: frm.doc.posting_date},
|
||||||
{fieldtype: "Small Text", fieldname: "user_remark", label: __("User Remark"), reqd: 1},
|
{fieldtype: "Small Text", fieldname: "user_remark", label: __("User Remark")},
|
||||||
{fieldtype: "Select", fieldname: "naming_series", label: __("Series"), reqd: 1,
|
{fieldtype: "Select", fieldname: "naming_series", label: __("Series"), reqd: 1,
|
||||||
options: naming_series_options, default: naming_series_default},
|
options: naming_series_options, default: naming_series_default},
|
||||||
]
|
]
|
||||||
|
@ -968,7 +968,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
|
|||||||
|
|
||||||
# The date used to retreive the exchange rate here is the date passed
|
# The date used to retreive the exchange rate here is the date passed
|
||||||
# in as an argument to this function.
|
# in as an argument to this function.
|
||||||
elif (not exchange_rate or exchange_rate==1) and account_currency and posting_date:
|
elif (not exchange_rate or flt(exchange_rate)==1) and account_currency and posting_date:
|
||||||
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
|
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
|
||||||
else:
|
else:
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
|
@ -201,7 +201,7 @@
|
|||||||
"fieldname": "reference_type",
|
"fieldname": "reference_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Reference Type",
|
"label": "Reference Type",
|
||||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting"
|
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "reference_name",
|
"fieldname": "reference_name",
|
||||||
@ -281,4 +281,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,12 @@ import unittest
|
|||||||
from frappe.utils import today, cint, flt, getdate
|
from frappe.utils import today, cint, flt, getdate
|
||||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
|
from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
|
||||||
from erpnext.accounts.party import get_dashboard_info
|
from erpnext.accounts.party import get_dashboard_info
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||||
|
|
||||||
class TestLoyaltyProgram(unittest.TestCase):
|
class TestLoyaltyProgram(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(self):
|
def setUpClass(self):
|
||||||
|
set_perpetual_inventory(0)
|
||||||
# create relevant item, customer, loyalty program, etc
|
# create relevant item, customer, loyalty program, etc
|
||||||
create_records()
|
create_records()
|
||||||
|
|
||||||
|
@ -32,8 +32,10 @@ class OpeningInvoiceCreationTool(Document):
|
|||||||
})
|
})
|
||||||
invoices_summary.update({company: _summary})
|
invoices_summary.update({company: _summary})
|
||||||
|
|
||||||
paid_amount.append(invoice.paid_amount)
|
if invoice.paid_amount:
|
||||||
outstanding_amount.append(invoice.outstanding_amount)
|
paid_amount.append(invoice.paid_amount)
|
||||||
|
if invoice.outstanding_amount:
|
||||||
|
outstanding_amount.append(invoice.outstanding_amount)
|
||||||
|
|
||||||
if paid_amount or outstanding_amount:
|
if paid_amount or outstanding_amount:
|
||||||
max_count.update({
|
max_count.update({
|
||||||
|
@ -554,7 +554,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frappe.flags.allocate_payment_amount = true;
|
frappe.flags.allocate_payment_amount = true;
|
||||||
frm.events.validate_filters_data(frm, filters);
|
frm.events.validate_filters_data(frm, filters);
|
||||||
frm.events.get_outstanding_documents(frm, filters);
|
frm.events.get_outstanding_documents(frm, filters);
|
||||||
}, __("Filters"), __("Get Outstanding Invoices"));
|
}, __("Filters"), __("Get Outstanding Documents"));
|
||||||
},
|
},
|
||||||
|
|
||||||
validate_filters_data: function(frm, filters) {
|
validate_filters_data: function(frm, filters) {
|
||||||
@ -652,14 +652,16 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
||||||
) {
|
) {
|
||||||
if(total_positive_outstanding > total_negative_outstanding)
|
if(total_positive_outstanding > total_negative_outstanding)
|
||||||
frm.set_value("paid_amount",
|
if (!frm.doc.paid_amount)
|
||||||
total_positive_outstanding - total_negative_outstanding);
|
frm.set_value("paid_amount",
|
||||||
|
total_positive_outstanding - total_negative_outstanding);
|
||||||
} else if (
|
} else if (
|
||||||
total_negative_outstanding &&
|
total_negative_outstanding &&
|
||||||
total_positive_outstanding < total_negative_outstanding
|
total_positive_outstanding < total_negative_outstanding
|
||||||
) {
|
) {
|
||||||
frm.set_value("received_amount",
|
if (!frm.doc.received_amount)
|
||||||
total_negative_outstanding - total_positive_outstanding);
|
frm.set_value("received_amount",
|
||||||
|
total_negative_outstanding - total_positive_outstanding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
"section_break_12",
|
"section_break_12",
|
||||||
|
"status",
|
||||||
"remarks",
|
"remarks",
|
||||||
"column_break_16",
|
"column_break_16",
|
||||||
"letter_head",
|
"letter_head",
|
||||||
@ -331,6 +332,7 @@
|
|||||||
"label": "Reference"
|
"label": "Reference"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:doc.docstatus==0",
|
||||||
"fieldname": "get_outstanding_invoice",
|
"fieldname": "get_outstanding_invoice",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"label": "Get Outstanding Invoice"
|
"label": "Get Outstanding Invoice"
|
||||||
@ -563,10 +565,18 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Draft",
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Status",
|
||||||
|
"options": "\nDraft\nSubmitted\nCancelled",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-05-27 15:53:21.108857",
|
"modified": "2019-12-08 13:02:30.016610",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
@ -61,6 +61,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.validate_duplicate_entry()
|
self.validate_duplicate_entry()
|
||||||
self.validate_allocated_amount()
|
self.validate_allocated_amount()
|
||||||
self.ensure_supplier_is_not_blocked()
|
self.ensure_supplier_is_not_blocked()
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.setup_party_account_field()
|
self.setup_party_account_field()
|
||||||
@ -70,6 +71,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.update_outstanding_amounts()
|
self.update_outstanding_amounts()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_expense_claim()
|
self.update_expense_claim()
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
@ -79,6 +81,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
self.update_expense_claim()
|
self.update_expense_claim()
|
||||||
self.delink_advance_entry_references()
|
self.delink_advance_entry_references()
|
||||||
|
self.set_status()
|
||||||
|
|
||||||
def update_outstanding_amounts(self):
|
def update_outstanding_amounts(self):
|
||||||
self.set_missing_ref_details(force=True)
|
self.set_missing_ref_details(force=True)
|
||||||
@ -275,6 +278,14 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
||||||
.format(d.reference_name, dr_or_cr))
|
.format(d.reference_name, dr_or_cr))
|
||||||
|
|
||||||
|
def set_status(self):
|
||||||
|
if self.docstatus == 2:
|
||||||
|
self.status = 'Cancelled'
|
||||||
|
elif self.docstatus == 1:
|
||||||
|
self.status = 'Submitted'
|
||||||
|
else:
|
||||||
|
self.status = 'Draft'
|
||||||
|
|
||||||
def set_amounts(self):
|
def set_amounts(self):
|
||||||
self.set_amounts_in_company_currency()
|
self.set_amounts_in_company_currency()
|
||||||
self.set_total_allocated_amount()
|
self.set_total_allocated_amount()
|
||||||
@ -900,7 +911,10 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
else:
|
else:
|
||||||
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
||||||
|
|
||||||
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
|
if dt not in ("Sales Invoice", "Purchase Invoice"):
|
||||||
|
party_account_currency = get_account_currency(party_account)
|
||||||
|
else:
|
||||||
|
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
|
||||||
|
|
||||||
# payment type
|
# payment type
|
||||||
if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \
|
if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \
|
||||||
@ -920,9 +934,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
grand_total = doc.rounded_total or doc.grand_total
|
grand_total = doc.rounded_total or doc.grand_total
|
||||||
outstanding_amount = doc.outstanding_amount
|
outstanding_amount = doc.outstanding_amount
|
||||||
elif dt in ("Expense Claim"):
|
elif dt in ("Expense Claim"):
|
||||||
grand_total = doc.total_sanctioned_amount
|
grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges
|
||||||
outstanding_amount = doc.total_sanctioned_amount \
|
outstanding_amount = doc.grand_total \
|
||||||
- doc.total_amount_reimbursed - flt(doc.total_advance_amount)
|
- doc.total_amount_reimbursed
|
||||||
elif dt == "Employee Advance":
|
elif dt == "Employee Advance":
|
||||||
grand_total = doc.advance_amount
|
grand_total = doc.advance_amount
|
||||||
outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
|
outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
|
||||||
|
@ -23,6 +23,8 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
if self.party_type in ["Customer", "Supplier"]:
|
if self.party_type in ["Customer", "Supplier"]:
|
||||||
dr_or_cr_notes = self.get_dr_or_cr_notes()
|
dr_or_cr_notes = self.get_dr_or_cr_notes()
|
||||||
|
else:
|
||||||
|
dr_or_cr_notes = []
|
||||||
|
|
||||||
self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes)
|
self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes)
|
||||||
|
|
||||||
@ -90,7 +92,8 @@ class PaymentReconciliation(Document):
|
|||||||
FROM `tab{doc}`, `tabGL Entry`
|
FROM `tab{doc}`, `tabGL Entry`
|
||||||
WHERE
|
WHERE
|
||||||
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
|
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
|
||||||
and `tab{doc}`.is_return = 1 and `tabGL Entry`.against_voucher_type = %(voucher_type)s
|
and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL
|
||||||
|
and `tabGL Entry`.against_voucher_type = %(voucher_type)s
|
||||||
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
|
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
|
||||||
and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s
|
and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s
|
||||||
GROUP BY `tab{doc}`.name
|
GROUP BY `tab{doc}`.name
|
||||||
|
@ -2,6 +2,16 @@ cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account
|
|||||||
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
|
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
|
||||||
cur_frm.add_fetch("payment_gateway_account", "message", "message")
|
cur_frm.add_fetch("payment_gateway_account", "message", "message")
|
||||||
|
|
||||||
|
frappe.ui.form.on("Payment Request", {
|
||||||
|
setup: function(frm) {
|
||||||
|
frm.set_query("party_type", function() {
|
||||||
|
return {
|
||||||
|
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
|
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
|
||||||
if (frm.doc.reference_doctype) {
|
if (frm.doc.reference_doctype) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
|
@ -350,13 +350,13 @@ def get_amount(ref_doc):
|
|||||||
if dt in ["Sales Order", "Purchase Order"]:
|
if dt in ["Sales Order", "Purchase Order"]:
|
||||||
grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
|
grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
|
||||||
|
|
||||||
if dt in ["Sales Invoice", "Purchase Invoice"]:
|
elif dt in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
if ref_doc.party_account_currency == ref_doc.currency:
|
if ref_doc.party_account_currency == ref_doc.currency:
|
||||||
grand_total = flt(ref_doc.outstanding_amount)
|
grand_total = flt(ref_doc.outstanding_amount)
|
||||||
else:
|
else:
|
||||||
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
|
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
|
||||||
|
|
||||||
if dt == "Fees":
|
elif dt == "Fees":
|
||||||
grand_total = ref_doc.outstanding_amount
|
grand_total = ref_doc.outstanding_amount
|
||||||
|
|
||||||
if grand_total > 0 :
|
if grand_total > 0 :
|
||||||
|
77
erpnext/accounts/doctype/pos_field/pos_field.json
Normal file
77
erpnext/accounts/doctype/pos_field/pos_field.json
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"creation": "2019-08-22 14:35:39.043242",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"fieldname",
|
||||||
|
"label",
|
||||||
|
"fieldtype",
|
||||||
|
"column_break_7",
|
||||||
|
"options",
|
||||||
|
"default_value",
|
||||||
|
"reqd",
|
||||||
|
"read_only"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "fieldname",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Fieldname"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fieldtype",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Fieldtype",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "label",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Label",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "options",
|
||||||
|
"fieldtype": "Text",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Options",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "reqd",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Mandatory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "read_only",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Read Only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_7",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "default_value",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Default Value"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"istable": 1,
|
||||||
|
"modified": "2019-08-23 13:59:34.025523",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "POS Field",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class SetupProgressAction(Document):
|
class POSField(Document):
|
||||||
pass
|
pass
|
@ -2,7 +2,46 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('POS Settings', {
|
frappe.ui.form.on('POS Settings', {
|
||||||
refresh: function() {
|
onload: function(frm) {
|
||||||
|
frm.trigger("get_invoice_fields");
|
||||||
|
},
|
||||||
|
|
||||||
|
use_pos_in_offline_mode: function(frm) {
|
||||||
|
frm.trigger("get_invoice_fields");
|
||||||
|
},
|
||||||
|
|
||||||
|
get_invoice_fields: function(frm) {
|
||||||
|
if (!frm.doc.use_pos_in_offline_mode) {
|
||||||
|
frappe.model.with_doctype("Sales Invoice", () => {
|
||||||
|
var fields = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
|
||||||
|
if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
|
||||||
|
d.fieldtype === 'Table') {
|
||||||
|
return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.meta.get_docfield("POS Field", "fieldname", frm.doc.name).options = [""].concat(fields);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frappe.meta.get_docfield("POS Field", "fieldname", frm.doc.name).options = [""];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on("POS Field", {
|
||||||
|
fieldname: function(frm, doctype, name) {
|
||||||
|
var doc = frappe.get_doc(doctype, name);
|
||||||
|
var df = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
|
||||||
|
return doc.fieldname == d.fieldname ? d : null;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
doc.label = df.label;
|
||||||
|
doc.reqd = df.reqd;
|
||||||
|
doc.options = df.options;
|
||||||
|
doc.fieldtype = df.fieldtype;
|
||||||
|
doc.default_value = df.default;
|
||||||
|
frm.refresh_field("fields");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,133 +1,68 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"creation": "2017-08-28 16:46:41.732676",
|
||||||
"allow_import": 0,
|
"doctype": "DocType",
|
||||||
"allow_rename": 0,
|
"editable_grid": 1,
|
||||||
"beta": 0,
|
"engine": "InnoDB",
|
||||||
"creation": "2017-08-28 16:46:41.732676",
|
"field_order": [
|
||||||
"custom": 0,
|
"use_pos_in_offline_mode",
|
||||||
"docstatus": 0,
|
"section_break_2",
|
||||||
"doctype": "DocType",
|
"fields"
|
||||||
"document_type": "",
|
],
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "0",
|
||||||
"allow_on_submit": 0,
|
"fieldname": "use_pos_in_offline_mode",
|
||||||
"bold": 0,
|
"fieldtype": "Check",
|
||||||
"collapsible": 0,
|
"label": "Use POS in Offline Mode"
|
||||||
"columns": 0,
|
},
|
||||||
"default": "0",
|
{
|
||||||
"fieldname": "use_pos_in_offline_mode",
|
"fieldname": "section_break_2",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Section Break"
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"depends_on": "eval:!doc.use_pos_in_offline_mode",
|
||||||
"in_filter": 0,
|
"fieldname": "fields",
|
||||||
"in_global_search": 0,
|
"fieldtype": "Table",
|
||||||
"in_list_view": 0,
|
"label": "POS Field",
|
||||||
"in_standard_filter": 0,
|
"options": "POS Field"
|
||||||
"label": "Use POS in Offline Mode",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"issingle": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2019-12-26 11:50:47.122997",
|
||||||
"idx": 0,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "POS Settings",
|
||||||
"is_submittable": 0,
|
"owner": "Administrator",
|
||||||
"issingle": 1,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2017-09-11 13:57:28.787023",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "POS Settings",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 0,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"role": "System Manager",
|
||||||
"delete": 0,
|
"share": 1,
|
||||||
"email": 1,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "System Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 0,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"role": "Accounts User",
|
||||||
"delete": 0,
|
"share": 1,
|
||||||
"email": 1,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Accounts User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 0,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"role": "Sales User",
|
||||||
"delete": 0,
|
"share": 1,
|
||||||
"email": 1,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Sales User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
"sort_field": "modified",
|
||||||
"read_only_onload": 0,
|
"sort_order": "DESC",
|
||||||
"show_name_in_global_search": 0,
|
"track_changes": 1
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -389,8 +389,7 @@
|
|||||||
"fieldname": "rate_or_discount",
|
"fieldname": "rate_or_discount",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Rate or Discount",
|
"label": "Rate or Discount",
|
||||||
"options": "\nRate\nDiscount Percentage\nDiscount Amount",
|
"options": "\nRate\nDiscount Percentage\nDiscount Amount"
|
||||||
"reqd": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Grand Total",
|
"default": "Grand Total",
|
||||||
@ -439,19 +438,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:!doc.mixed_conditions",
|
"depends_on": "eval:!doc.mixed_conditions && doc.apply_on != 'Transaction'",
|
||||||
"fieldname": "same_item",
|
"fieldname": "same_item",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Same Item"
|
"label": "Same Item"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:!doc.same_item || doc.mixed_conditions",
|
"depends_on": "eval:(!doc.same_item || doc.apply_on == 'Transaction') || doc.mixed_conditions",
|
||||||
"fieldname": "free_item",
|
"fieldname": "free_item",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Free Item",
|
"label": "Free Item",
|
||||||
"options": "Item"
|
"options": "Item"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "free_qty",
|
"fieldname": "free_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Qty"
|
"label": "Qty"
|
||||||
@ -554,7 +554,7 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-10-15 12:39:40.399792",
|
"modified": "2019-12-18 17:29:22.957077",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
@ -34,8 +34,7 @@ class PricingRule(Document):
|
|||||||
|
|
||||||
def validate_duplicate_apply_on(self):
|
def validate_duplicate_apply_on(self):
|
||||||
field = apply_on_dict.get(self.apply_on)
|
field = apply_on_dict.get(self.apply_on)
|
||||||
values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field)]
|
values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field) if field]
|
||||||
|
|
||||||
if len(values) != len(set(values)):
|
if len(values) != len(set(values)):
|
||||||
frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on))
|
frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on))
|
||||||
|
|
||||||
@ -48,6 +47,9 @@ class PricingRule(Document):
|
|||||||
if tocheck and not self.get(tocheck):
|
if tocheck and not self.get(tocheck):
|
||||||
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
||||||
|
|
||||||
|
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
|
||||||
|
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
|
||||||
|
|
||||||
def validate_applicable_for_selling_or_buying(self):
|
def validate_applicable_for_selling_or_buying(self):
|
||||||
if not self.selling and not self.buying:
|
if not self.selling and not self.buying:
|
||||||
throw(_("Atleast one of the Selling or Buying must be selected"))
|
throw(_("Atleast one of the Selling or Buying must be selected"))
|
||||||
@ -181,8 +183,9 @@ def get_serial_no_for_item(args):
|
|||||||
item_details.serial_no = get_serial_no(args)
|
item_details.serial_no = get_serial_no(args)
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
|
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rules
|
from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
|
||||||
|
get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule)
|
||||||
|
|
||||||
if isinstance(doc, string_types):
|
if isinstance(doc, string_types):
|
||||||
doc = json.loads(doc)
|
doc = json.loads(doc)
|
||||||
@ -209,6 +212,57 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
|
|||||||
item_details, args.get('item_code'))
|
item_details, args.get('item_code'))
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
|
update_args_for_pricing_rule(args)
|
||||||
|
|
||||||
|
pricing_rules = (get_applied_pricing_rules(args)
|
||||||
|
if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc))
|
||||||
|
|
||||||
|
if pricing_rules:
|
||||||
|
rules = []
|
||||||
|
|
||||||
|
for pricing_rule in pricing_rules:
|
||||||
|
if not pricing_rule: continue
|
||||||
|
|
||||||
|
if isinstance(pricing_rule, string_types):
|
||||||
|
pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
|
||||||
|
pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule)
|
||||||
|
|
||||||
|
if pricing_rule.get('suggestion'): continue
|
||||||
|
|
||||||
|
item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0)
|
||||||
|
item_details.price_or_product_discount = pricing_rule.get("price_or_product_discount")
|
||||||
|
|
||||||
|
rules.append(get_pricing_rule_details(args, pricing_rule))
|
||||||
|
|
||||||
|
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
|
||||||
|
item_details.update({
|
||||||
|
'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items),
|
||||||
|
'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other)
|
||||||
|
if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
|
||||||
|
})
|
||||||
|
|
||||||
|
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
|
||||||
|
return item_details
|
||||||
|
|
||||||
|
if not pricing_rule.validate_applied_rule:
|
||||||
|
if pricing_rule.price_or_product_discount == "Price":
|
||||||
|
apply_price_discount_rule(pricing_rule, item_details, args)
|
||||||
|
else:
|
||||||
|
get_product_discount_rule(pricing_rule, item_details, doc)
|
||||||
|
|
||||||
|
item_details.has_pricing_rule = 1
|
||||||
|
|
||||||
|
item_details.pricing_rules = ','.join([d.pricing_rule for d in rules])
|
||||||
|
|
||||||
|
if not doc: return item_details
|
||||||
|
|
||||||
|
elif args.get("pricing_rules"):
|
||||||
|
item_details = remove_pricing_rule_for_item(args.get("pricing_rules"),
|
||||||
|
item_details, args.get('item_code'))
|
||||||
|
|
||||||
|
return item_details
|
||||||
|
|
||||||
|
def update_args_for_pricing_rule(args):
|
||||||
if not (args.item_group and args.brand):
|
if not (args.item_group and args.brand):
|
||||||
try:
|
try:
|
||||||
args.item_group, args.brand = frappe.get_cached_value("Item", args.item_code, ["item_group", "brand"])
|
args.item_group, args.brand = frappe.get_cached_value("Item", args.item_code, ["item_group", "brand"])
|
||||||
@ -235,56 +289,16 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
|
|||||||
args.supplier_group = frappe.get_cached_value("Supplier", args.supplier, "supplier_group")
|
args.supplier_group = frappe.get_cached_value("Supplier", args.supplier, "supplier_group")
|
||||||
args.customer = args.customer_group = args.territory = None
|
args.customer = args.customer_group = args.territory = None
|
||||||
|
|
||||||
pricing_rules = get_pricing_rules(args, doc)
|
|
||||||
|
|
||||||
if pricing_rules:
|
|
||||||
rules = []
|
|
||||||
|
|
||||||
for pricing_rule in pricing_rules:
|
|
||||||
if not pricing_rule or pricing_rule.get('suggestion'): continue
|
|
||||||
|
|
||||||
item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0)
|
|
||||||
|
|
||||||
rules.append(get_pricing_rule_details(args, pricing_rule))
|
|
||||||
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
|
|
||||||
return item_details
|
|
||||||
|
|
||||||
if (not pricing_rule.validate_applied_rule and
|
|
||||||
pricing_rule.price_or_product_discount == "Price"):
|
|
||||||
apply_price_discount_pricing_rule(pricing_rule, item_details, args)
|
|
||||||
|
|
||||||
item_details.has_pricing_rule = 1
|
|
||||||
|
|
||||||
# if discount is applied on the rate and not on price list rate
|
|
||||||
# if price_list_rate:
|
|
||||||
# set_discount_amount(price_list_rate, item_details)
|
|
||||||
|
|
||||||
item_details.pricing_rules = ','.join([d.pricing_rule for d in rules])
|
|
||||||
|
|
||||||
if not doc: return item_details
|
|
||||||
|
|
||||||
for rule in rules:
|
|
||||||
doc.append('pricing_rules', rule)
|
|
||||||
|
|
||||||
elif args.get("pricing_rules"):
|
|
||||||
item_details = remove_pricing_rule_for_item(args.get("pricing_rules"),
|
|
||||||
item_details, args.get('item_code'))
|
|
||||||
|
|
||||||
return item_details
|
|
||||||
|
|
||||||
def get_pricing_rule_details(args, pricing_rule):
|
def get_pricing_rule_details(args, pricing_rule):
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
'pricing_rule': pricing_rule.name,
|
'pricing_rule': pricing_rule.name,
|
||||||
'rate_or_discount': pricing_rule.rate_or_discount,
|
'rate_or_discount': pricing_rule.rate_or_discount,
|
||||||
'margin_type': pricing_rule.margin_type,
|
'margin_type': pricing_rule.margin_type,
|
||||||
'item_code': pricing_rule.item_code or args.get("item_code"),
|
'item_code': args.get("item_code"),
|
||||||
'child_docname': args.get('child_docname')
|
'child_docname': args.get('child_docname')
|
||||||
})
|
})
|
||||||
|
|
||||||
def apply_price_discount_pricing_rule(pricing_rule, item_details, args):
|
def apply_price_discount_rule(pricing_rule, item_details, args):
|
||||||
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
||||||
|
|
||||||
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
|
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
|
||||||
@ -327,10 +341,10 @@ def set_discount_amount(rate, item_details):
|
|||||||
item_details.rate = rate
|
item_details.rate = rate
|
||||||
|
|
||||||
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import get_apply_on_and_items
|
from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rule_items
|
||||||
for d in pricing_rules.split(','):
|
for d in pricing_rules.split(','):
|
||||||
if not d or not frappe.db.exists("Pricing Rule", d): continue
|
if not d or not frappe.db.exists("Pricing Rule", d): continue
|
||||||
pricing_rule = frappe.get_doc('Pricing Rule', d)
|
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
||||||
|
|
||||||
if pricing_rule.price_or_product_discount == 'Price':
|
if pricing_rule.price_or_product_discount == 'Price':
|
||||||
if pricing_rule.rate_or_discount == 'Discount Percentage':
|
if pricing_rule.rate_or_discount == 'Discount Percentage':
|
||||||
@ -348,8 +362,9 @@ def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
|||||||
else pricing_rule.get('free_item'))
|
else pricing_rule.get('free_item'))
|
||||||
|
|
||||||
if pricing_rule.get("mixed_conditions") or pricing_rule.get("apply_rule_on_other"):
|
if pricing_rule.get("mixed_conditions") or pricing_rule.get("apply_rule_on_other"):
|
||||||
apply_on, items = get_apply_on_and_items(pricing_rule, item_details)
|
items = get_pricing_rule_items(pricing_rule)
|
||||||
item_details.apply_on = apply_on
|
item_details.apply_on = (frappe.scrub(pricing_rule.apply_rule_on_other)
|
||||||
|
if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
|
||||||
item_details.applied_on_items = ','.join(items)
|
item_details.applied_on_items = ','.join(items)
|
||||||
|
|
||||||
item_details.pricing_rules = ''
|
item_details.pricing_rules = ''
|
||||||
|
@ -7,7 +7,8 @@ from __future__ import unicode_literals
|
|||||||
import frappe, copy, json
|
import frappe, copy, json
|
||||||
from frappe import throw, _
|
from frappe import throw, _
|
||||||
from six import string_types
|
from six import string_types
|
||||||
from frappe.utils import flt, cint, get_datetime
|
from frappe.utils import flt, cint, get_datetime, get_link_to_form, today
|
||||||
|
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
||||||
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
from erpnext.stock.get_item_details import get_conversion_factor
|
from erpnext.stock.get_item_details import get_conversion_factor
|
||||||
|
|
||||||
@ -173,10 +174,11 @@ def filter_pricing_rules(args, pricing_rules, doc=None):
|
|||||||
|
|
||||||
if (field and pricing_rules[0].get('other_' + field) != args.get(field)): return
|
if (field and pricing_rules[0].get('other_' + field) != args.get(field)): return
|
||||||
|
|
||||||
pr_doc = frappe.get_doc('Pricing Rule', pricing_rules[0].name)
|
pr_doc = frappe.get_cached_doc('Pricing Rule', pricing_rules[0].name)
|
||||||
|
|
||||||
if pricing_rules[0].mixed_conditions and doc:
|
if pricing_rules[0].mixed_conditions and doc:
|
||||||
stock_qty, amount = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
|
stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
|
||||||
|
pricing_rules[0].apply_rule_on_other_items = items
|
||||||
|
|
||||||
elif pricing_rules[0].is_cumulative:
|
elif pricing_rules[0].is_cumulative:
|
||||||
items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
|
items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
|
||||||
@ -282,7 +284,7 @@ def filter_pricing_rules_for_qty_amount(qty, rate, pricing_rules, args=None):
|
|||||||
status = True
|
status = True
|
||||||
|
|
||||||
# if user has created item price against the transaction UOM
|
# if user has created item price against the transaction UOM
|
||||||
if rule.get("uom") == args.get("uom"):
|
if args and rule.get("uom") == args.get("uom"):
|
||||||
conversion_factor = 1.0
|
conversion_factor = 1.0
|
||||||
|
|
||||||
if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor)
|
if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor)
|
||||||
@ -339,17 +341,19 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
|
|||||||
sum_qty += data[0]
|
sum_qty += data[0]
|
||||||
sum_amt += data[1]
|
sum_amt += data[1]
|
||||||
|
|
||||||
return sum_qty, sum_amt
|
return sum_qty, sum_amt, items
|
||||||
|
|
||||||
def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
|
def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
|
||||||
for d in get_pricing_rule_items(pr_doc):
|
items = get_pricing_rule_items(pr_doc)
|
||||||
for row in doc.items:
|
|
||||||
if d == row.get(frappe.scrub(pr_doc.apply_on)):
|
|
||||||
pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
|
|
||||||
row.get("amount"), pricing_rules, row)
|
|
||||||
|
|
||||||
if pricing_rules and pricing_rules[0]:
|
for row in doc.items:
|
||||||
return pricing_rules
|
if row.get(frappe.scrub(pr_doc.apply_rule_on_other)) in items:
|
||||||
|
pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
|
||||||
|
row.get("amount"), pricing_rules, row)
|
||||||
|
|
||||||
|
if pricing_rules and pricing_rules[0]:
|
||||||
|
pricing_rules[0].apply_rule_on_other_items = items
|
||||||
|
return pricing_rules
|
||||||
|
|
||||||
def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
|
def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
|
||||||
sum_qty, sum_amt = [0, 0]
|
sum_qty, sum_amt = [0, 0]
|
||||||
@ -397,38 +401,15 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
|
|||||||
|
|
||||||
return [sum_qty, sum_amt]
|
return [sum_qty, sum_amt]
|
||||||
|
|
||||||
def validate_pricing_rules(doc):
|
def apply_pricing_rule_on_transaction(doc):
|
||||||
validate_pricing_rule_on_transactions(doc)
|
|
||||||
|
|
||||||
for d in doc.items:
|
|
||||||
validate_pricing_rule_on_items(doc, d)
|
|
||||||
|
|
||||||
doc.calculate_taxes_and_totals()
|
|
||||||
|
|
||||||
def validate_pricing_rule_on_items(doc, item_row, do_not_validate = False):
|
|
||||||
value = 0
|
|
||||||
for pricing_rule in get_applied_pricing_rules(doc, item_row):
|
|
||||||
pr_doc = frappe.get_doc('Pricing Rule', pricing_rule)
|
|
||||||
|
|
||||||
if pr_doc.get('apply_on') == 'Transaction': continue
|
|
||||||
|
|
||||||
if pr_doc.get('price_or_product_discount') == 'Product':
|
|
||||||
apply_pricing_rule_for_free_items(doc, pr_doc)
|
|
||||||
else:
|
|
||||||
for field in ['discount_percentage', 'discount_amount', 'rate']:
|
|
||||||
if not pr_doc.get(field): continue
|
|
||||||
|
|
||||||
value += pr_doc.get(field)
|
|
||||||
apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate)
|
|
||||||
|
|
||||||
def validate_pricing_rule_on_transactions(doc):
|
|
||||||
conditions = "apply_on = 'Transaction'"
|
conditions = "apply_on = 'Transaction'"
|
||||||
|
|
||||||
values = {}
|
values = {}
|
||||||
conditions = get_other_conditions(conditions, values, doc)
|
conditions = get_other_conditions(conditions, values, doc)
|
||||||
|
|
||||||
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
||||||
where {conditions} """.format(conditions = conditions), values, as_dict=1)
|
where {conditions} and `tabPricing Rule`.disable = 0
|
||||||
|
""".format(conditions = conditions), values, as_dict=1)
|
||||||
|
|
||||||
if pricing_rules:
|
if pricing_rules:
|
||||||
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
||||||
@ -440,98 +421,83 @@ def validate_pricing_rule_on_transactions(doc):
|
|||||||
doc.set('apply_discount_on', d.apply_discount_on)
|
doc.set('apply_discount_on', d.apply_discount_on)
|
||||||
|
|
||||||
for field in ['additional_discount_percentage', 'discount_amount']:
|
for field in ['additional_discount_percentage', 'discount_amount']:
|
||||||
if not d.get(field): continue
|
|
||||||
|
|
||||||
pr_field = ('discount_percentage'
|
pr_field = ('discount_percentage'
|
||||||
if field == 'additional_discount_percentage' else field)
|
if field == 'additional_discount_percentage' else field)
|
||||||
|
|
||||||
|
if not d.get(pr_field): continue
|
||||||
|
|
||||||
if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
|
if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
|
||||||
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
||||||
.format(doc.name))
|
.format(doc.name))
|
||||||
else:
|
else:
|
||||||
doc.set(field, d.get(pr_field))
|
doc.set(field, d.get(pr_field))
|
||||||
elif d.price_or_product_discount == 'Product':
|
|
||||||
apply_pricing_rule_for_free_items(doc, d)
|
|
||||||
|
|
||||||
def get_applied_pricing_rules(doc, item_row):
|
doc.calculate_taxes_and_totals()
|
||||||
|
elif d.price_or_product_discount == 'Product':
|
||||||
|
item_details = frappe._dict({'parenttype': doc.doctype})
|
||||||
|
get_product_discount_rule(d, item_details, doc)
|
||||||
|
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||||
|
doc.set_missing_values()
|
||||||
|
|
||||||
|
def get_applied_pricing_rules(item_row):
|
||||||
return (item_row.get("pricing_rules").split(',')
|
return (item_row.get("pricing_rules").split(',')
|
||||||
if item_row.get("pricing_rules") else [])
|
if item_row.get("pricing_rules") else [])
|
||||||
|
|
||||||
def apply_pricing_rule_for_free_items(doc, pricing_rule):
|
def get_product_discount_rule(pricing_rule, item_details, doc=None):
|
||||||
if pricing_rule.get('free_item'):
|
free_item = (pricing_rule.free_item
|
||||||
|
if not pricing_rule.same_item or pricing_rule.apply_on == 'Transaction' else item_details.item_code)
|
||||||
|
|
||||||
|
if not free_item:
|
||||||
|
frappe.throw(_("Free item not set in the pricing rule {0}")
|
||||||
|
.format(get_link_to_form("Pricing Rule", pricing_rule.name)))
|
||||||
|
|
||||||
|
item_details.free_item_data = {
|
||||||
|
'item_code': free_item,
|
||||||
|
'qty': pricing_rule.free_qty or 1,
|
||||||
|
'rate': pricing_rule.free_item_rate or 0,
|
||||||
|
'price_list_rate': pricing_rule.free_item_rate or 0,
|
||||||
|
'is_free_item': 1
|
||||||
|
}
|
||||||
|
|
||||||
|
item_data = frappe.get_cached_value('Item', free_item, ['item_name',
|
||||||
|
'description', 'stock_uom'], as_dict=1)
|
||||||
|
|
||||||
|
item_details.free_item_data.update(item_data)
|
||||||
|
item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
|
||||||
|
item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item,
|
||||||
|
item_details.free_item_data['uom']).get("conversion_factor", 1)
|
||||||
|
|
||||||
|
if item_details.get("parenttype") == 'Purchase Order':
|
||||||
|
item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today()
|
||||||
|
|
||||||
|
if item_details.get("parenttype") == 'Sales Order':
|
||||||
|
item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
|
||||||
|
|
||||||
|
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
|
||||||
|
if pricing_rule_args.get('item_code'):
|
||||||
items = [d.item_code for d in doc.items
|
items = [d.item_code for d in doc.items
|
||||||
if d.item_code == (d.item_code
|
if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item]
|
||||||
if pricing_rule.get('same_item') else pricing_rule.get('free_item')) and d.is_free_item]
|
|
||||||
|
|
||||||
if not items:
|
if not items:
|
||||||
doc.append('items', {
|
doc.append('items', pricing_rule_args)
|
||||||
'item_code': pricing_rule.get('free_item'),
|
|
||||||
'qty': pricing_rule.get('free_qty'),
|
|
||||||
'uom': pricing_rule.get('free_item_uom'),
|
|
||||||
'rate': pricing_rule.get('free_item_rate'),
|
|
||||||
'is_free_item': 1
|
|
||||||
})
|
|
||||||
|
|
||||||
doc.set_missing_values()
|
|
||||||
|
|
||||||
def apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate=False):
|
|
||||||
apply_on, items = get_apply_on_and_items(pr_doc, item_row)
|
|
||||||
|
|
||||||
rule_applied = {}
|
|
||||||
|
|
||||||
for item in doc.get("items"):
|
|
||||||
if item.get(apply_on) in items:
|
|
||||||
if not item.pricing_rules:
|
|
||||||
item.pricing_rules = item_row.pricing_rules
|
|
||||||
|
|
||||||
for field in ['discount_percentage', 'discount_amount', 'rate']:
|
|
||||||
if not pr_doc.get(field): continue
|
|
||||||
|
|
||||||
key = (item.name, item.pricing_rules)
|
|
||||||
if not pr_doc.validate_applied_rule:
|
|
||||||
rule_applied[key] = 1
|
|
||||||
item.set(field, value)
|
|
||||||
elif item.get(field) < value:
|
|
||||||
if not do_not_validate and item.idx == item_row.idx:
|
|
||||||
rule_applied[key] = 0
|
|
||||||
frappe.msgprint(_("Row {0}: user has not applied rule <b>{1}</b> on the item <b>{2}</b>")
|
|
||||||
.format(item.idx, pr_doc.title, item.item_code))
|
|
||||||
|
|
||||||
if rule_applied and doc.get("pricing_rules"):
|
|
||||||
for d in doc.get("pricing_rules"):
|
|
||||||
key = (d.child_docname, d.pricing_rule)
|
|
||||||
if key in rule_applied:
|
|
||||||
d.rule_applied = 1
|
|
||||||
|
|
||||||
def get_apply_on_and_items(pr_doc, item_row):
|
|
||||||
# for mixed or other items conditions
|
|
||||||
apply_on = frappe.scrub(pr_doc.get('apply_on'))
|
|
||||||
items = (get_pricing_rule_items(pr_doc)
|
|
||||||
if pr_doc.mixed_conditions else [item_row.get(apply_on)])
|
|
||||||
|
|
||||||
if pr_doc.apply_rule_on_other:
|
|
||||||
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
|
|
||||||
items = [pr_doc.get(apply_on)]
|
|
||||||
|
|
||||||
return apply_on, items
|
|
||||||
|
|
||||||
def get_pricing_rule_items(pr_doc):
|
def get_pricing_rule_items(pr_doc):
|
||||||
|
apply_on_data = []
|
||||||
apply_on = frappe.scrub(pr_doc.get('apply_on'))
|
apply_on = frappe.scrub(pr_doc.get('apply_on'))
|
||||||
|
|
||||||
pricing_rule_apply_on = apply_on_table.get(pr_doc.get('apply_on'))
|
pricing_rule_apply_on = apply_on_table.get(pr_doc.get('apply_on'))
|
||||||
|
|
||||||
return [item.get(apply_on) for item in pr_doc.get(pricing_rule_apply_on)] or []
|
for d in pr_doc.get(pricing_rule_apply_on):
|
||||||
|
if apply_on == 'item_group':
|
||||||
|
get_child_item_groups(d.get(apply_on))
|
||||||
|
else:
|
||||||
|
apply_on_data.append(d.get(apply_on))
|
||||||
|
|
||||||
@frappe.whitelist()
|
if pr_doc.apply_rule_on_other:
|
||||||
def validate_pricing_rule_for_different_cond(doc):
|
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
|
||||||
if isinstance(doc, string_types):
|
apply_on_data.append(pr_doc.get("other_" + apply_on))
|
||||||
doc = json.loads(doc)
|
|
||||||
|
|
||||||
doc = frappe.get_doc(doc)
|
return list(set(apply_on_data))
|
||||||
for d in doc.get("items"):
|
|
||||||
validate_pricing_rule_on_items(doc, d, True)
|
|
||||||
|
|
||||||
return doc
|
|
||||||
|
|
||||||
def validate_coupon_code(coupon_name):
|
def validate_coupon_code(coupon_name):
|
||||||
from frappe.utils import today,getdate
|
from frappe.utils import today,getdate
|
||||||
|
@ -330,23 +330,6 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
asset: function(frm, cdt, cdn) {
|
|
||||||
var row = locals[cdt][cdn];
|
|
||||||
if(row.asset) {
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.assets.doctype.asset_category.asset_category.get_asset_category_account",
|
|
||||||
args: {
|
|
||||||
"asset": row.asset,
|
|
||||||
"fieldname": "fixed_asset_account",
|
|
||||||
"account": row.expense_account
|
|
||||||
},
|
|
||||||
callback: function(r, rt) {
|
|
||||||
frappe.model.set_value(cdt, cdn, "expense_account", r.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
||||||
@ -399,21 +382,11 @@ cur_frm.fields_dict['items'].grid.get_field("item_code").get_query = function(do
|
|||||||
|
|
||||||
cur_frm.fields_dict['credit_to'].get_query = function(doc) {
|
cur_frm.fields_dict['credit_to'].get_query = function(doc) {
|
||||||
// filter on Account
|
// filter on Account
|
||||||
if (doc.supplier) {
|
return {
|
||||||
return {
|
filters: {
|
||||||
filters: {
|
'account_type': 'Payable',
|
||||||
'account_type': 'Payable',
|
'is_group': 0,
|
||||||
'is_group': 0,
|
'company': doc.company
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'report_type': 'Balance Sheet',
|
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,19 +403,7 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn)
|
|||||||
cur_frm.set_query("expense_account", "items", function(doc) {
|
cur_frm.set_query("expense_account", "items", function(doc) {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.get_expense_account",
|
query: "erpnext.controllers.queries.get_expense_account",
|
||||||
filters: {'company': doc.company}
|
filters: {'company': doc.company }
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cur_frm.set_query("asset", "items", function(doc, cdt, cdn) {
|
|
||||||
var d = locals[cdt][cdn];
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'item_code': d.item_code,
|
|
||||||
'docstatus': 1,
|
|
||||||
'company': doc.company,
|
|
||||||
'status': 'Submitted'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-21 16:16:39",
|
"creation": "2013-05-21 16:16:39",
|
||||||
@ -417,6 +418,7 @@
|
|||||||
"fieldname": "contact_email",
|
"fieldname": "contact_email",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Contact Email",
|
"label": "Contact Email",
|
||||||
|
"options": "Email",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -705,7 +707,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "other_charges_calculation",
|
"fieldname": "other_charges_calculation",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Long Text",
|
||||||
"label": "Taxes and Charges Calculation",
|
"label": "Taxes and Charges Calculation",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldtype": "HTML",
|
"oldfieldtype": "HTML",
|
||||||
@ -1287,7 +1289,8 @@
|
|||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-09-17 22:31:42.666601",
|
"links": [],
|
||||||
|
"modified": "2019-12-30 19:13:49.610538",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
@ -18,13 +18,14 @@ from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entri
|
|||||||
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||||
from erpnext.buying.utils import check_on_hold_or_closed_status
|
from erpnext.buying.utils import check_on_hold_or_closed_status
|
||||||
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
|
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
|
||||||
from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_disabled
|
from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
|
||||||
unlink_inter_company_doc
|
unlink_inter_company_doc
|
||||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||||
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import get_item_account_wise_additional_cost
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"items": "templates/form_grid/item_grid.html"
|
"items": "templates/form_grid/item_grid.html"
|
||||||
@ -97,7 +98,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.set_against_expense_account()
|
self.set_against_expense_account()
|
||||||
self.validate_write_off_account()
|
self.validate_write_off_account()
|
||||||
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
|
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
|
||||||
self.validate_fixed_asset()
|
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.validate_purchase_receipt_if_update_stock()
|
self.validate_purchase_receipt_if_update_stock()
|
||||||
@ -225,6 +225,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
# in case of auto inventory accounting,
|
# in case of auto inventory accounting,
|
||||||
# expense account is always "Stock Received But Not Billed" for a stock item
|
# expense account is always "Stock Received But Not Billed" for a stock item
|
||||||
# except epening entry, drop-ship entry and fixed asset items
|
# except epening entry, drop-ship entry and fixed asset items
|
||||||
|
if item.item_code:
|
||||||
|
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
||||||
|
|
||||||
if auto_accounting_for_stock and item.item_code in stock_items \
|
if auto_accounting_for_stock and item.item_code in stock_items \
|
||||||
and self.is_opening == 'No' and not item.is_fixed_asset \
|
and self.is_opening == 'No' and not item.is_fixed_asset \
|
||||||
@ -235,12 +237,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.expense_account = warehouse_account[item.warehouse]["account"]
|
item.expense_account = warehouse_account[item.warehouse]["account"]
|
||||||
else:
|
else:
|
||||||
item.expense_account = stock_not_billed_account
|
item.expense_account = stock_not_billed_account
|
||||||
elif item.is_fixed_asset and is_cwip_accounting_disabled():
|
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
||||||
if not item.asset:
|
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||||
frappe.throw(_("Row {0}: asset is required for item {1}")
|
|
||||||
.format(item.idx, item.item_code))
|
|
||||||
|
|
||||||
item.expense_account = get_asset_category_account(item.asset, 'fixed_asset_account',
|
|
||||||
company = self.company)
|
company = self.company)
|
||||||
elif item.is_fixed_asset and item.pr_detail:
|
elif item.is_fixed_asset and item.pr_detail:
|
||||||
item.expense_account = asset_received_but_not_billed
|
item.expense_account = asset_received_but_not_billed
|
||||||
@ -250,7 +248,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
def set_against_expense_account(self):
|
def set_against_expense_account(self):
|
||||||
against_accounts = []
|
against_accounts = []
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.expense_account not in against_accounts:
|
if item.expense_account and (item.expense_account not in against_accounts):
|
||||||
against_accounts.append(item.expense_account)
|
against_accounts.append(item.expense_account)
|
||||||
|
|
||||||
self.against_expense_account = ",".join(against_accounts)
|
self.against_expense_account = ",".join(against_accounts)
|
||||||
@ -391,7 +389,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
self.make_supplier_gl_entry(gl_entries)
|
self.make_supplier_gl_entry(gl_entries)
|
||||||
self.make_item_gl_entries(gl_entries)
|
self.make_item_gl_entries(gl_entries)
|
||||||
if not is_cwip_accounting_disabled():
|
|
||||||
|
if self.check_asset_cwip_enabled():
|
||||||
self.get_asset_gl_entry(gl_entries)
|
self.get_asset_gl_entry(gl_entries)
|
||||||
|
|
||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
@ -404,6 +403,15 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
|
def check_asset_cwip_enabled(self):
|
||||||
|
# Check if there exists any item with cwip accounting enabled in it's asset category
|
||||||
|
for item in self.get("items"):
|
||||||
|
if item.item_code and item.is_fixed_asset:
|
||||||
|
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
||||||
|
if is_cwip_accounting_enabled(asset_category):
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
def make_supplier_gl_entry(self, gl_entries):
|
def make_supplier_gl_entry(self, gl_entries):
|
||||||
# Checked both rounding_adjustment and rounded_total
|
# Checked both rounding_adjustment and rounded_total
|
||||||
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
||||||
@ -436,15 +444,23 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if self.update_stock and self.auto_accounting_for_stock:
|
if self.update_stock and self.auto_accounting_for_stock:
|
||||||
warehouse_account = get_warehouse_account_map(self.company)
|
warehouse_account = get_warehouse_account_map(self.company)
|
||||||
|
|
||||||
|
landed_cost_entries = get_item_account_wise_additional_cost(self.name)
|
||||||
|
|
||||||
voucher_wise_stock_value = {}
|
voucher_wise_stock_value = {}
|
||||||
if self.update_stock:
|
if self.update_stock:
|
||||||
for d in frappe.get_all('Stock Ledger Entry',
|
for d in frappe.get_all('Stock Ledger Entry',
|
||||||
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
|
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
|
||||||
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
|
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
|
||||||
|
|
||||||
|
valuation_tax_accounts = [d.account_head for d in self.get("taxes")
|
||||||
|
if d.category in ('Valuation', 'Total and Valuation')
|
||||||
|
and flt(d.base_tax_amount_after_discount_amount)]
|
||||||
|
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if flt(item.base_net_amount):
|
if flt(item.base_net_amount):
|
||||||
account_currency = get_account_currency(item.expense_account)
|
account_currency = get_account_currency(item.expense_account)
|
||||||
|
if item.item_code:
|
||||||
|
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
||||||
|
|
||||||
if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
|
if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
|
||||||
# warehouse account
|
# warehouse account
|
||||||
@ -463,15 +479,16 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Amount added through landed-cost-voucher
|
# Amount added through landed-cost-voucher
|
||||||
if flt(item.landed_cost_voucher_amount):
|
if landed_cost_entries:
|
||||||
gl_entries.append(self.get_gl_dict({
|
for account, amount in iteritems(landed_cost_entries[(item.item_code, item.name)]):
|
||||||
"account": expenses_included_in_valuation,
|
gl_entries.append(self.get_gl_dict({
|
||||||
"against": item.expense_account,
|
"account": account,
|
||||||
"cost_center": item.cost_center,
|
"against": item.expense_account,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
"cost_center": item.cost_center,
|
||||||
"credit": flt(item.landed_cost_voucher_amount),
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
"project": item.project
|
"credit": flt(amount),
|
||||||
}, item=item))
|
"project": item.project
|
||||||
|
}, item=item))
|
||||||
|
|
||||||
# sub-contracting warehouse
|
# sub-contracting warehouse
|
||||||
if flt(item.rm_supp_cost):
|
if flt(item.rm_supp_cost):
|
||||||
@ -486,31 +503,61 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
"credit": flt(item.rm_supp_cost)
|
"credit": flt(item.rm_supp_cost)
|
||||||
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
|
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
|
||||||
elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()):
|
|
||||||
|
|
||||||
|
elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)):
|
||||||
expense_account = (item.expense_account
|
expense_account = (item.expense_account
|
||||||
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
||||||
|
|
||||||
gl_entries.append(
|
if not item.is_fixed_asset:
|
||||||
self.get_gl_dict({
|
amount = flt(item.base_net_amount, item.precision("base_net_amount"))
|
||||||
|
else:
|
||||||
|
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
|
||||||
|
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": expense_account,
|
"account": expense_account,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
"debit": amount,
|
||||||
"debit_in_account_currency": (flt(item.base_net_amount,
|
|
||||||
item.precision("base_net_amount")) if account_currency==self.company_currency
|
|
||||||
else flt(item.net_amount, item.precision("net_amount"))),
|
|
||||||
"cost_center": item.cost_center,
|
"cost_center": item.cost_center,
|
||||||
"project": item.project
|
"project": item.project
|
||||||
}, account_currency, item=item)
|
}, account_currency, item=item))
|
||||||
)
|
|
||||||
|
# If asset is bought through this document and not linked to PR
|
||||||
|
if self.update_stock and item.landed_cost_voucher_amount:
|
||||||
|
expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
|
||||||
|
# Amount added through landed-cost-voucher
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": expenses_included_in_asset_valuation,
|
||||||
|
"against": expense_account,
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"credit": flt(item.landed_cost_voucher_amount),
|
||||||
|
"project": item.project
|
||||||
|
}, item=item))
|
||||||
|
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": expense_account,
|
||||||
|
"against": expenses_included_in_asset_valuation,
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"debit": flt(item.landed_cost_voucher_amount),
|
||||||
|
"project": item.project
|
||||||
|
}, item=item))
|
||||||
|
|
||||||
|
# update gross amount of asset bought through this document
|
||||||
|
assets = frappe.db.get_all('Asset',
|
||||||
|
filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
|
||||||
|
)
|
||||||
|
for asset in assets:
|
||||||
|
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
||||||
|
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
|
||||||
|
|
||||||
if self.auto_accounting_for_stock and self.is_opening == "No" and \
|
if self.auto_accounting_for_stock and self.is_opening == "No" and \
|
||||||
item.item_code in stock_items and item.item_tax_amount:
|
item.item_code in stock_items and item.item_tax_amount:
|
||||||
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
|
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
|
||||||
if item.purchase_receipt:
|
if item.purchase_receipt and valuation_tax_accounts:
|
||||||
negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
|
negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
|
||||||
where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""",
|
where voucher_type='Purchase Receipt' and voucher_no=%s and account in %s""",
|
||||||
(item.purchase_receipt, self.expenses_included_in_valuation))
|
(item.purchase_receipt, valuation_tax_accounts))
|
||||||
|
|
||||||
if not negative_expense_booked_in_pr:
|
if not negative_expense_booked_in_pr:
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@ -527,27 +574,27 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.precision("item_tax_amount"))
|
item.precision("item_tax_amount"))
|
||||||
|
|
||||||
def get_asset_gl_entry(self, gl_entries):
|
def get_asset_gl_entry(self, gl_entries):
|
||||||
|
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
||||||
|
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||||
|
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.is_fixed_asset:
|
if item.is_fixed_asset:
|
||||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
|
||||||
|
|
||||||
asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
|
asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
|
||||||
base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
|
base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
|
||||||
|
|
||||||
if (not item.expense_account or frappe.db.get_value('Account',
|
item_exp_acc_type = frappe.db.get_value('Account', item.expense_account, 'account_type')
|
||||||
item.expense_account, 'account_type') not in ['Asset Received But Not Billed', 'Fixed Asset']):
|
if (not item.expense_account or item_exp_acc_type not in ['Asset Received But Not Billed', 'Fixed Asset']):
|
||||||
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
|
||||||
item.expense_account = arbnb_account
|
item.expense_account = arbnb_account
|
||||||
|
|
||||||
if not self.update_stock:
|
if not self.update_stock:
|
||||||
asset_rbnb_currency = get_account_currency(item.expense_account)
|
arbnb_currency = get_account_currency(item.expense_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": item.expense_account,
|
"account": item.expense_account,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"debit": base_asset_amount,
|
"debit": base_asset_amount,
|
||||||
"debit_in_account_currency": (base_asset_amount
|
"debit_in_account_currency": (base_asset_amount
|
||||||
if asset_rbnb_currency == self.company_currency else asset_amount),
|
if arbnb_currency == self.company_currency else asset_amount),
|
||||||
"cost_center": item.cost_center
|
"cost_center": item.cost_center
|
||||||
}, item=item))
|
}, item=item))
|
||||||
|
|
||||||
@ -564,8 +611,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.item_tax_amount / self.conversion_rate)
|
item.item_tax_amount / self.conversion_rate)
|
||||||
}, item=item))
|
}, item=item))
|
||||||
else:
|
else:
|
||||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company)
|
||||||
item.asset, company = self.company)
|
|
||||||
|
|
||||||
cwip_account_currency = get_account_currency(cwip_account)
|
cwip_account_currency = get_account_currency(cwip_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
@ -591,6 +637,36 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.item_tax_amount / self.conversion_rate)
|
item.item_tax_amount / self.conversion_rate)
|
||||||
}, item=item))
|
}, item=item))
|
||||||
|
|
||||||
|
# When update stock is checked
|
||||||
|
# Assets are bought through this document then it will be linked to this document
|
||||||
|
if self.update_stock:
|
||||||
|
if flt(item.landed_cost_voucher_amount):
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": eiiav_account,
|
||||||
|
"against": cwip_account,
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"credit": flt(item.landed_cost_voucher_amount),
|
||||||
|
"project": item.project
|
||||||
|
}, item=item))
|
||||||
|
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": cwip_account,
|
||||||
|
"against": eiiav_account,
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
|
"debit": flt(item.landed_cost_voucher_amount),
|
||||||
|
"project": item.project
|
||||||
|
}, item=item))
|
||||||
|
|
||||||
|
# update gross amount of assets bought through this document
|
||||||
|
assets = frappe.db.get_all('Asset',
|
||||||
|
filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
|
||||||
|
)
|
||||||
|
for asset in assets:
|
||||||
|
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
||||||
|
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
|
||||||
|
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
|
def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
|
||||||
@ -641,14 +717,14 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if account_currency==self.company_currency \
|
if account_currency==self.company_currency \
|
||||||
else tax.tax_amount_after_discount_amount,
|
else tax.tax_amount_after_discount_amount,
|
||||||
"cost_center": tax.cost_center
|
"cost_center": tax.cost_center
|
||||||
}, account_currency)
|
}, account_currency, item=tax)
|
||||||
)
|
)
|
||||||
# accumulate valuation tax
|
# accumulate valuation tax
|
||||||
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
||||||
if self.auto_accounting_for_stock and not tax.cost_center:
|
if self.auto_accounting_for_stock and not tax.cost_center:
|
||||||
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
|
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
|
||||||
valuation_tax.setdefault(tax.cost_center, 0)
|
valuation_tax.setdefault(tax.name, 0)
|
||||||
valuation_tax[tax.cost_center] += \
|
valuation_tax[tax.name] += \
|
||||||
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
|
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
|
||||||
|
|
||||||
if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax:
|
if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax:
|
||||||
@ -658,36 +734,38 @@ class PurchaseInvoice(BuyingController):
|
|||||||
total_valuation_amount = sum(valuation_tax.values())
|
total_valuation_amount = sum(valuation_tax.values())
|
||||||
amount_including_divisional_loss = self.negative_expense_to_be_booked
|
amount_including_divisional_loss = self.negative_expense_to_be_booked
|
||||||
i = 1
|
i = 1
|
||||||
for cost_center, amount in iteritems(valuation_tax):
|
for tax in self.get("taxes"):
|
||||||
if i == len(valuation_tax):
|
if valuation_tax.get(tax.name):
|
||||||
applicable_amount = amount_including_divisional_loss
|
if i == len(valuation_tax):
|
||||||
else:
|
applicable_amount = amount_including_divisional_loss
|
||||||
applicable_amount = self.negative_expense_to_be_booked * (amount / total_valuation_amount)
|
else:
|
||||||
amount_including_divisional_loss -= applicable_amount
|
applicable_amount = self.negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount)
|
||||||
|
amount_including_divisional_loss -= applicable_amount
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": self.expenses_included_in_valuation,
|
"account": tax.account_head,
|
||||||
"cost_center": cost_center,
|
"cost_center": tax.cost_center,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"credit": applicable_amount,
|
"credit": applicable_amount,
|
||||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
"remarks": self.remarks or _("Accounting Entry for Stock"),
|
||||||
})
|
}, item=tax)
|
||||||
)
|
)
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
if self.auto_accounting_for_stock and self.update_stock and valuation_tax:
|
if self.auto_accounting_for_stock and self.update_stock and valuation_tax:
|
||||||
for cost_center, amount in iteritems(valuation_tax):
|
for tax in self.get("taxes"):
|
||||||
gl_entries.append(
|
if valuation_tax.get(tax.name):
|
||||||
self.get_gl_dict({
|
gl_entries.append(
|
||||||
"account": self.expenses_included_in_valuation,
|
self.get_gl_dict({
|
||||||
"cost_center": cost_center,
|
"account": tax.account_head,
|
||||||
"against": self.supplier,
|
"cost_center": tax.cost_center,
|
||||||
"credit": amount,
|
"against": self.supplier,
|
||||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
"credit": valuation_tax[tax.name],
|
||||||
})
|
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||||
)
|
}, item=tax)
|
||||||
|
)
|
||||||
|
|
||||||
def make_payment_gl_entries(self, gl_entries):
|
def make_payment_gl_entries(self, gl_entries):
|
||||||
# Make Cash GL Entries
|
# Make Cash GL Entries
|
||||||
@ -752,7 +830,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
if self.rounding_adjustment:
|
# if rounding adjustment in small and conversion rate is also small then
|
||||||
|
# base_rounding_adjustment may become zero due to small precision
|
||||||
|
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
|
||||||
|
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
|
||||||
|
if self.rounding_adjustment and self.base_rounding_adjustment:
|
||||||
round_off_account, round_off_cost_center = \
|
round_off_account, round_off_cost_center = \
|
||||||
get_round_off_account_and_cost_center(self.company)
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ frappe.listview_settings['Purchase Invoice'] = {
|
|||||||
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||||
"currency", "is_return", "release_date", "on_hold"],
|
"currency", "is_return", "release_date", "on_hold"],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) {
|
if( (flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
|
||||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
|
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"];
|
||||||
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
||||||
if(cint(doc.on_hold) && !doc.release_date) {
|
if(cint(doc.on_hold) && !doc.release_date) {
|
||||||
return [__("On Hold"), "darkgrey"];
|
return [__("On Hold"), "darkgrey"];
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{% include "erpnext/regional/india/taxes.js" %}
|
||||||
|
|
||||||
|
erpnext.setup_auto_gst_taxation('Purchase Invoice');
|
@ -10,7 +10,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_ent
|
|||||||
from frappe.utils import cint, flt, today, nowdate, add_days
|
from frappe.utils import cint, flt, today, nowdate, add_days
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
||||||
test_records as pr_test_records
|
test_records as pr_test_records, make_purchase_receipt, get_taxes
|
||||||
from erpnext.controllers.accounts_controller import get_payment_terms
|
from erpnext.controllers.accounts_controller import get_payment_terms
|
||||||
from erpnext.exceptions import InvalidCurrency
|
from erpnext.exceptions import InvalidCurrency
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
|
||||||
@ -57,16 +57,11 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
|
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
|
||||||
|
|
||||||
def test_gl_entries_with_perpetual_inventory(self):
|
def test_gl_entries_with_perpetual_inventory(self):
|
||||||
pi = frappe.copy_doc(test_records[1])
|
pi = make_purchase_invoice(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1", get_taxes_and_charges=True, qty=10)
|
||||||
set_perpetual_inventory(1, pi.company)
|
|
||||||
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1)
|
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1)
|
||||||
pi.insert()
|
|
||||||
pi.submit()
|
|
||||||
|
|
||||||
self.check_gle_for_pi(pi.name)
|
self.check_gle_for_pi(pi.name)
|
||||||
|
|
||||||
set_perpetual_inventory(0, pi.company)
|
|
||||||
|
|
||||||
def test_terms_added_after_save(self):
|
def test_terms_added_after_save(self):
|
||||||
pi = frappe.copy_doc(test_records[1])
|
pi = frappe.copy_doc(test_records[1])
|
||||||
pi.insert()
|
pi.insert()
|
||||||
@ -196,32 +191,33 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(pi.on_hold, 0)
|
self.assertEqual(pi.on_hold, 0)
|
||||||
|
|
||||||
def test_gl_entries_with_perpetual_inventory_against_pr(self):
|
def test_gl_entries_with_perpetual_inventory_against_pr(self):
|
||||||
pr = frappe.copy_doc(pr_test_records[0])
|
|
||||||
set_perpetual_inventory(1, pr.company)
|
|
||||||
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
|
|
||||||
pr.submit()
|
|
||||||
|
|
||||||
pi = frappe.copy_doc(test_records[1])
|
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", get_taxes_and_charges=True,)
|
||||||
for d in pi.get("items"):
|
|
||||||
|
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
|
||||||
|
|
||||||
|
pi = make_purchase_invoice(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1", get_taxes_and_charges=True, qty=10,do_not_save= "True")
|
||||||
|
|
||||||
|
for d in pi.items:
|
||||||
d.purchase_receipt = pr.name
|
d.purchase_receipt = pr.name
|
||||||
|
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
|
||||||
self.check_gle_for_pi(pi.name)
|
self.check_gle_for_pi(pi.name)
|
||||||
|
|
||||||
set_perpetual_inventory(0, pr.company)
|
|
||||||
|
|
||||||
def check_gle_for_pi(self, pi):
|
def check_gle_for_pi(self, pi):
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
gl_entries = frappe.db.sql("""select account, sum(debit) as debit, sum(credit) as credit
|
||||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||||
order by account asc""", pi, as_dict=1)
|
group by account""", pi, as_dict=1)
|
||||||
|
|
||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
expected_values = dict((d[0], d) for d in [
|
expected_values = dict((d[0], d) for d in [
|
||||||
["_Test Payable - _TC", 0, 720],
|
["Creditors - TCP1", 0, 720],
|
||||||
["Stock Received But Not Billed - _TC", 500.0, 0],
|
["Stock Received But Not Billed - TCP1", 500.0, 0],
|
||||||
["_Test Account Shipping Charges - _TC", 100.0, 0],
|
["_Test Account Shipping Charges - TCP1", 100.0, 0.0],
|
||||||
["_Test Account VAT - _TC", 120.0, 0],
|
["_Test Account VAT - TCP1", 120.0, 0]
|
||||||
])
|
])
|
||||||
|
|
||||||
for i, gle in enumerate(gl_entries):
|
for i, gle in enumerate(gl_entries):
|
||||||
@ -524,10 +520,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
self.assertFalse(gle)
|
self.assertFalse(gle)
|
||||||
|
|
||||||
def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self):
|
def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self):
|
||||||
set_perpetual_inventory()
|
|
||||||
|
|
||||||
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
|
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
|
||||||
posting_time=frappe.utils.nowtime())
|
posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - TCP1", company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||||
debit_in_account_currency, credit_in_account_currency
|
debit_in_account_currency, credit_in_account_currency
|
||||||
@ -548,9 +543,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(expected_gl_entries[gle.account][2], gle.credit)
|
self.assertEqual(expected_gl_entries[gle.account][2], gle.credit)
|
||||||
|
|
||||||
def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self):
|
def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self):
|
||||||
set_perpetual_inventory()
|
|
||||||
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
|
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
|
||||||
posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - _TC", is_paid=1)
|
posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - TCP1", is_paid=1, company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, account_currency, sum(debit) as debit,
|
gl_entries = frappe.db.sql("""select account, account_currency, sum(debit) as debit,
|
||||||
sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
|
sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
|
||||||
@ -563,7 +558,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
expected_gl_entries = dict((d[0], d) for d in [
|
expected_gl_entries = dict((d[0], d) for d in [
|
||||||
[pi.credit_to, 250.0, 250.0],
|
[pi.credit_to, 250.0, 250.0],
|
||||||
[stock_in_hand_account, 250.0, 0.0],
|
[stock_in_hand_account, 250.0, 0.0],
|
||||||
["Cash - _TC", 0.0, 250.0]
|
["Cash - TCP1", 0.0, 250.0]
|
||||||
])
|
])
|
||||||
|
|
||||||
for i, gle in enumerate(gl_entries):
|
for i, gle in enumerate(gl_entries):
|
||||||
@ -630,6 +625,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(pi.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
|
self.assertEqual(pi.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
|
||||||
|
|
||||||
def test_rejected_serial_no(self):
|
def test_rejected_serial_no(self):
|
||||||
|
set_perpetual_inventory(0)
|
||||||
pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1,
|
pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1,
|
||||||
rejected_qty=1, rate=500, update_stock=1,
|
rejected_qty=1, rate=500, update_stock=1,
|
||||||
rejected_warehouse = "_Test Rejected Warehouse - _TC")
|
rejected_warehouse = "_Test Rejected Warehouse - _TC")
|
||||||
@ -881,7 +877,7 @@ def make_purchase_invoice(**args):
|
|||||||
pi.is_return = args.is_return
|
pi.is_return = args.is_return
|
||||||
pi.return_against = args.return_against
|
pi.return_against = args.return_against
|
||||||
pi.is_subcontracted = args.is_subcontracted or "No"
|
pi.is_subcontracted = args.is_subcontracted or "No"
|
||||||
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
|
pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
|
||||||
|
|
||||||
pi.append("items", {
|
pi.append("items", {
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
@ -890,14 +886,21 @@ def make_purchase_invoice(**args):
|
|||||||
"received_qty": args.received_qty or 0,
|
"received_qty": args.received_qty or 0,
|
||||||
"rejected_qty": args.rejected_qty or 0,
|
"rejected_qty": args.rejected_qty or 0,
|
||||||
"rate": args.rate or 50,
|
"rate": args.rate or 50,
|
||||||
|
'expense_account': args.expense_account or '_Test Account Cost for Goods Sold - _TC',
|
||||||
"conversion_factor": 1.0,
|
"conversion_factor": 1.0,
|
||||||
"serial_no": args.serial_no,
|
"serial_no": args.serial_no,
|
||||||
"stock_uom": "_Test UOM",
|
"stock_uom": "_Test UOM",
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||||
"project": args.project,
|
"project": args.project,
|
||||||
"rejected_warehouse": args.rejected_warehouse or "",
|
"rejected_warehouse": args.rejected_warehouse or "",
|
||||||
"rejected_serial_no": args.rejected_serial_no or ""
|
"rejected_serial_no": args.rejected_serial_no or ""
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if args.get_taxes_and_charges:
|
||||||
|
taxes = get_taxes()
|
||||||
|
for tax in taxes:
|
||||||
|
pi.append("taxes", tax)
|
||||||
|
|
||||||
if not args.do_not_save:
|
if not args.do_not_save:
|
||||||
pi.insert()
|
pi.insert()
|
||||||
if not args.do_not_submit:
|
if not args.do_not_submit:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-05-22 12:43:10",
|
"creation": "2013-05-22 12:43:10",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -71,8 +72,8 @@
|
|||||||
"expense_account",
|
"expense_account",
|
||||||
"col_break5",
|
"col_break5",
|
||||||
"is_fixed_asset",
|
"is_fixed_asset",
|
||||||
"asset",
|
|
||||||
"asset_location",
|
"asset_location",
|
||||||
|
"asset_category",
|
||||||
"deferred_expense_section",
|
"deferred_expense_section",
|
||||||
"deferred_expense_account",
|
"deferred_expense_account",
|
||||||
"service_stop_date",
|
"service_stop_date",
|
||||||
@ -116,6 +117,8 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.item_name",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
@ -414,6 +417,7 @@
|
|||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "batch_no",
|
"fieldname": "batch_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Batch No",
|
"label": "Batch No",
|
||||||
@ -425,12 +429,14 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "serial_no",
|
"fieldname": "serial_no",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
"label": "Serial No",
|
"label": "Serial No",
|
||||||
"no_copy": 1
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "rejected_serial_no",
|
"fieldname": "rejected_serial_no",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
"label": "Rejected Serial No",
|
"label": "Rejected Serial No",
|
||||||
@ -502,7 +508,8 @@
|
|||||||
"depends_on": "enable_deferred_expense",
|
"depends_on": "enable_deferred_expense",
|
||||||
"fieldname": "service_stop_date",
|
"fieldname": "service_stop_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service Stop Date"
|
"label": "Service Stop Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -518,13 +525,15 @@
|
|||||||
"depends_on": "enable_deferred_expense",
|
"depends_on": "enable_deferred_expense",
|
||||||
"fieldname": "service_start_date",
|
"fieldname": "service_start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service Start Date"
|
"label": "Service Start Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "enable_deferred_expense",
|
"depends_on": "enable_deferred_expense",
|
||||||
"fieldname": "service_end_date",
|
"fieldname": "service_end_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service End Date"
|
"label": "Service End Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "reference",
|
"fieldname": "reference",
|
||||||
@ -615,6 +624,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"fetch_from": "item_code.is_fixed_asset",
|
||||||
"fieldname": "is_fixed_asset",
|
"fieldname": "is_fixed_asset",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@ -623,14 +633,6 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"depends_on": "is_fixed_asset",
|
|
||||||
"fieldname": "asset",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Asset",
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Asset"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"depends_on": "is_fixed_asset",
|
"depends_on": "is_fixed_asset",
|
||||||
"fieldname": "asset_location",
|
"fieldname": "asset_location",
|
||||||
@ -676,7 +678,7 @@
|
|||||||
"fieldname": "pr_detail",
|
"fieldname": "pr_detail",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "PR Detail",
|
"label": "Purchase Receipt Detail",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "pr_detail",
|
"oldfieldname": "pr_detail",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
@ -754,11 +756,22 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Manufacturer Part Number",
|
"label": "Manufacturer Part Number",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "is_fixed_asset",
|
||||||
|
"fetch_from": "item_code.asset_category",
|
||||||
|
"fieldname": "asset_category",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_preview": 1,
|
||||||
|
"label": "Asset Category",
|
||||||
|
"options": "Asset Category",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-09-17 22:32:05.984240",
|
"links": [],
|
||||||
|
"modified": "2019-12-04 12:23:17.046413",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
|
@ -1,300 +1,108 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"allow_rename": 1,
|
||||||
"allow_rename": 1,
|
"creation": "2013-01-10 16:34:08",
|
||||||
"autoname": "field:title",
|
"description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
|
||||||
"beta": 0,
|
"doctype": "DocType",
|
||||||
"creation": "2013-01-10 16:34:08",
|
"document_type": "Setup",
|
||||||
"custom": 0,
|
"field_order": [
|
||||||
"description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
|
"title",
|
||||||
"docstatus": 0,
|
"is_default",
|
||||||
"doctype": "DocType",
|
"disabled",
|
||||||
"document_type": "Setup",
|
"column_break4",
|
||||||
"editable_grid": 0,
|
"company",
|
||||||
|
"tax_category",
|
||||||
|
"section_break6",
|
||||||
|
"taxes"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "title",
|
||||||
"bold": 0,
|
"fieldtype": "Data",
|
||||||
"collapsible": 0,
|
"label": "Title",
|
||||||
"columns": 0,
|
"no_copy": 1,
|
||||||
"fieldname": "title",
|
"oldfieldname": "title",
|
||||||
"fieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"hidden": 0,
|
"reqd": 1
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "title",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "is_default",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "is_default",
|
"label": "Default"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Default",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "disabled",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "disabled",
|
"label": "Disabled"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Disabled",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "column_break4",
|
||||||
"bold": 0,
|
"fieldtype": "Column Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break4",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "company",
|
||||||
"bold": 0,
|
"fieldtype": "Link",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"columns": 0,
|
"in_standard_filter": 1,
|
||||||
"fieldname": "company",
|
"label": "Company",
|
||||||
"fieldtype": "Link",
|
"options": "Company",
|
||||||
"hidden": 0,
|
"remember_last_selected_value": 1,
|
||||||
"ignore_user_permissions": 0,
|
"reqd": 1
|
||||||
"ignore_xss_filter": 0,
|
},
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "section_break6",
|
||||||
"bold": 0,
|
"fieldtype": "Section Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break6",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "taxes",
|
||||||
"bold": 0,
|
"fieldtype": "Table",
|
||||||
"collapsible": 0,
|
"label": "Purchase Taxes and Charges",
|
||||||
"columns": 0,
|
"oldfieldname": "purchase_tax_details",
|
||||||
"fieldname": "taxes",
|
"oldfieldtype": "Table",
|
||||||
"fieldtype": "Table",
|
"options": "Purchase Taxes and Charges"
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "tax_category",
|
||||||
"in_filter": 0,
|
"fieldtype": "Link",
|
||||||
"in_list_view": 0,
|
"label": "Tax Category",
|
||||||
"in_standard_filter": 0,
|
"options": "Tax Category"
|
||||||
"label": "Purchase Taxes and Charges",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "purchase_tax_details",
|
|
||||||
"oldfieldtype": "Table",
|
|
||||||
"options": "Purchase Taxes and Charges",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"icon": "fa fa-money",
|
||||||
"hide_toolbar": 0,
|
"idx": 1,
|
||||||
"icon": "fa fa-money",
|
"modified": "2019-11-25 13:05:26.220275",
|
||||||
"idx": 1,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "Purchase Taxes and Charges Template",
|
||||||
|
"owner": "wasim@webnotestech.com",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2016-11-07 05:18:44.095798",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Purchase Taxes and Charges Template",
|
|
||||||
"owner": "wasim@webnotestech.com",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 0,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"report": 1,
|
||||||
"delete": 0,
|
"role": "Purchase Manager"
|
||||||
"email": 1,
|
},
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Purchase Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Purchase Master Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Purchase Master Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"read": 1,
|
||||||
"apply_user_permissions": 0,
|
"role": "Purchase User"
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Purchase User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_order": "DESC",
|
||||||
"read_only": 0,
|
"track_changes": 1
|
||||||
"read_only_onload": 0,
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -357,14 +357,11 @@ def get_customer_wise_price_list():
|
|||||||
|
|
||||||
def get_bin_data(pos_profile):
|
def get_bin_data(pos_profile):
|
||||||
itemwise_bin_data = {}
|
itemwise_bin_data = {}
|
||||||
cond = "1=1"
|
filters = { 'actual_qty': ['>', 0] }
|
||||||
if pos_profile.get('warehouse'):
|
if pos_profile.get('warehouse'):
|
||||||
cond = "warehouse = %(warehouse)s"
|
filters.update({ 'warehouse': pos_profile.get('warehouse') })
|
||||||
|
|
||||||
bin_data = frappe.db.sql(""" select item_code, warehouse, actual_qty from `tabBin`
|
bin_data = frappe.db.get_all('Bin', fields = ['item_code', 'warehouse', 'actual_qty'], filters=filters)
|
||||||
where actual_qty > 0 and {cond}""".format(cond=cond), {
|
|
||||||
'warehouse': frappe.db.escape(pos_profile.get('warehouse'))
|
|
||||||
}, as_dict=1)
|
|
||||||
|
|
||||||
for bins in bin_data:
|
for bins in bin_data:
|
||||||
if bins.item_code not in itemwise_bin_data:
|
if bins.item_code not in itemwise_bin_data:
|
||||||
@ -402,14 +399,21 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
|
|||||||
for docs in doc_list:
|
for docs in doc_list:
|
||||||
for name, doc in iteritems(docs):
|
for name, doc in iteritems(docs):
|
||||||
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
|
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
|
||||||
validate_records(doc)
|
if isinstance(doc, dict):
|
||||||
si_doc = frappe.new_doc('Sales Invoice')
|
validate_records(doc)
|
||||||
si_doc.offline_pos_name = name
|
si_doc = frappe.new_doc('Sales Invoice')
|
||||||
si_doc.update(doc)
|
si_doc.offline_pos_name = name
|
||||||
si_doc.set_posting_time = 1
|
si_doc.update(doc)
|
||||||
si_doc.customer = get_customer_id(doc)
|
si_doc.set_posting_time = 1
|
||||||
si_doc.due_date = doc.get('posting_date')
|
si_doc.customer = get_customer_id(doc)
|
||||||
name_list = submit_invoice(si_doc, name, doc, name_list)
|
si_doc.due_date = doc.get('posting_date')
|
||||||
|
name_list = submit_invoice(si_doc, name, doc, name_list)
|
||||||
|
else:
|
||||||
|
doc.due_date = doc.get('posting_date')
|
||||||
|
doc.customer = get_customer_id(doc)
|
||||||
|
doc.set_posting_time = 1
|
||||||
|
doc.offline_pos_name = name
|
||||||
|
name_list = submit_invoice(doc, name, doc, name_list)
|
||||||
else:
|
else:
|
||||||
name_list.append(name)
|
name_list.append(name)
|
||||||
|
|
||||||
@ -543,11 +547,15 @@ def make_address(args, customer):
|
|||||||
|
|
||||||
def make_email_queue(email_queue):
|
def make_email_queue(email_queue):
|
||||||
name_list = []
|
name_list = []
|
||||||
|
|
||||||
for key, data in iteritems(email_queue):
|
for key, data in iteritems(email_queue):
|
||||||
name = frappe.db.get_value('Sales Invoice', {'offline_pos_name': key}, 'name')
|
name = frappe.db.get_value('Sales Invoice', {'offline_pos_name': key}, 'name')
|
||||||
|
if not name: continue
|
||||||
|
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
sender = frappe.session.user
|
sender = frappe.session.user
|
||||||
print_format = "POS Invoice" if not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')) else None
|
print_format = "POS Invoice" if not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')) else None
|
||||||
|
|
||||||
attachments = [frappe.attach_print('Sales Invoice', name, print_format=print_format)]
|
attachments = [frappe.attach_print('Sales Invoice', name, print_format=print_format)]
|
||||||
|
|
||||||
make(subject=data.get('subject'), content=data.get('content'), recipients=data.get('recipients'),
|
make(subject=data.get('subject'), content=data.get('content'), recipients=data.get('recipients'),
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
{% include "erpnext/regional/india/taxes.js" %}
|
||||||
|
|
||||||
|
erpnext.setup_auto_gst_taxation('Sales Invoice');
|
||||||
|
|
||||||
frappe.ui.form.on("Sales Invoice", {
|
frappe.ui.form.on("Sales Invoice", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query('transporter', function() {
|
frm.set_query('transporter', function() {
|
||||||
@ -35,4 +39,5 @@ frappe.ui.form.on("Sales Invoice", {
|
|||||||
}, __("Make"));
|
}, __("Make"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -556,22 +556,11 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.set_query("debit_to", function(doc) {
|
cur_frm.set_query("debit_to", function(doc) {
|
||||||
// filter on Account
|
return {
|
||||||
if (doc.customer) {
|
filters: {
|
||||||
return {
|
'account_type': 'Receivable',
|
||||||
filters: {
|
'is_group': 0,
|
||||||
'account_type': 'Receivable',
|
'company': doc.company
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'report_type': 'Balance Sheet',
|
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -697,8 +686,8 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
if (frm.doc.company)
|
if (frm.doc.company)
|
||||||
{
|
{
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"frappe.contacts.doctype.address.address.get_default_address",
|
method:"erpnext.setup.doctype.company.company.get_default_company_address",
|
||||||
args:{ doctype:'Company',name:frm.doc.company},
|
args:{name:frm.doc.company, existing_address: frm.doc.company_address},
|
||||||
callback: function(r){
|
callback: function(r){
|
||||||
if (r.message){
|
if (r.message){
|
||||||
frm.set_value("company_address",r.message)
|
frm.set_value("company_address",r.message)
|
||||||
@ -789,22 +778,21 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
method: "frappe.client.get_value",
|
method: "frappe.client.get_value",
|
||||||
args:{
|
args:{
|
||||||
doctype: "Patient",
|
doctype: "Patient",
|
||||||
filters: {"name": frm.doc.patient},
|
filters: {
|
||||||
|
"name": frm.doc.patient
|
||||||
|
},
|
||||||
fieldname: "customer"
|
fieldname: "customer"
|
||||||
},
|
},
|
||||||
callback:function(patient_customer) {
|
callback:function(r) {
|
||||||
if(patient_customer){
|
if(r && r.message.customer){
|
||||||
frm.set_value("customer", patient_customer.message.customer);
|
frm.set_value("customer", r.message.customer);
|
||||||
frm.refresh_fields();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
frm.set_value("customer", '');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if (frappe.boot.active_domains.includes("Healthcare")){
|
if (frappe.boot.active_domains.includes("Healthcare")){
|
||||||
frm.set_df_property("patient", "hidden", 0);
|
frm.set_df_property("patient", "hidden", 0);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-24 19:29:05",
|
"creation": "2013-05-24 19:29:05",
|
||||||
@ -774,7 +775,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "other_charges_calculation",
|
"fieldname": "other_charges_calculation",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Long Text",
|
||||||
"label": "Taxes and Charges Calculation",
|
"label": "Taxes and Charges Calculation",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldtype": "HTML",
|
"oldfieldtype": "HTML",
|
||||||
@ -1567,7 +1568,8 @@
|
|||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 181,
|
"idx": 181,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-10-05 21:39:49.235990",
|
"links": [],
|
||||||
|
"modified": "2019-12-30 19:15:59.580414",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
@ -90,6 +90,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.validate_account_for_change_amount()
|
self.validate_account_for_change_amount()
|
||||||
self.validate_fixed_asset()
|
self.validate_fixed_asset()
|
||||||
self.set_income_account_for_fixed_assets()
|
self.set_income_account_for_fixed_assets()
|
||||||
|
self.validate_item_cost_centers()
|
||||||
validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_invoice_reference)
|
validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_invoice_reference)
|
||||||
|
|
||||||
if cint(self.is_pos):
|
if cint(self.is_pos):
|
||||||
@ -136,6 +137,22 @@ class SalesInvoice(SellingController):
|
|||||||
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
|
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
|
||||||
validate_loyalty_points(self, self.loyalty_points)
|
validate_loyalty_points(self, self.loyalty_points)
|
||||||
|
|
||||||
|
def validate_fixed_asset(self):
|
||||||
|
for d in self.get("items"):
|
||||||
|
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
|
||||||
|
asset = frappe.get_doc("Asset", d.asset)
|
||||||
|
if self.doctype == "Sales Invoice" and self.docstatus == 1:
|
||||||
|
if self.update_stock:
|
||||||
|
frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
|
||||||
|
|
||||||
|
elif asset.status in ("Scrapped", "Cancelled", "Sold"):
|
||||||
|
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
|
||||||
|
|
||||||
|
def validate_item_cost_centers(self):
|
||||||
|
for item in self.items:
|
||||||
|
cost_center_company = frappe.get_cached_value("Cost Center", item.cost_center, "company")
|
||||||
|
if cost_center_company != self.company:
|
||||||
|
frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company)))
|
||||||
|
|
||||||
def before_save(self):
|
def before_save(self):
|
||||||
set_account_for_mode_of_payment(self)
|
set_account_for_mode_of_payment(self)
|
||||||
@ -338,7 +355,8 @@ class SalesInvoice(SellingController):
|
|||||||
"print_format": print_format,
|
"print_format": print_format,
|
||||||
"allow_edit_rate": pos.get("allow_user_to_edit_rate"),
|
"allow_edit_rate": pos.get("allow_user_to_edit_rate"),
|
||||||
"allow_edit_discount": pos.get("allow_user_to_edit_discount"),
|
"allow_edit_discount": pos.get("allow_user_to_edit_discount"),
|
||||||
"campaign": pos.get("campaign")
|
"campaign": pos.get("campaign"),
|
||||||
|
"allow_print_before_pay": pos.get("allow_print_before_pay")
|
||||||
}
|
}
|
||||||
|
|
||||||
def update_time_sheet(self, sales_invoice):
|
def update_time_sheet(self, sales_invoice):
|
||||||
@ -525,9 +543,7 @@ class SalesInvoice(SellingController):
|
|||||||
for i in dic:
|
for i in dic:
|
||||||
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
if (d.item_code and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
||||||
if (d.item_code and is_stock_item == 1\
|
|
||||||
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
|
||||||
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
||||||
|
|
||||||
|
|
||||||
@ -686,7 +702,6 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
|
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
|
||||||
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
|
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
|
||||||
|
|
||||||
if not gl_entries:
|
if not gl_entries:
|
||||||
gl_entries = self.get_gl_entries()
|
gl_entries = self.get_gl_entries()
|
||||||
|
|
||||||
@ -944,7 +959,7 @@ class SalesInvoice(SellingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
|
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
|
||||||
round_off_account, round_off_cost_center = \
|
round_off_account, round_off_cost_center = \
|
||||||
get_round_off_account_and_cost_center(self.company)
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
@ -992,10 +1007,8 @@ class SalesInvoice(SellingController):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
for serial_no in item.serial_no.split("\n"):
|
for serial_no in item.serial_no.split("\n"):
|
||||||
if serial_no and frappe.db.exists('Serial No', serial_no):
|
if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
|
||||||
sno = frappe.get_doc('Serial No', serial_no)
|
frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
|
||||||
sno.sales_invoice = invoice
|
|
||||||
sno.db_update()
|
|
||||||
|
|
||||||
def validate_serial_numbers(self):
|
def validate_serial_numbers(self):
|
||||||
"""
|
"""
|
||||||
@ -1041,12 +1054,18 @@ class SalesInvoice(SellingController):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
for serial_no in item.serial_no.split("\n"):
|
for serial_no in item.serial_no.split("\n"):
|
||||||
sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice")
|
serial_no_details = frappe.db.get_value("Serial No", serial_no,
|
||||||
if sales_invoice and self.name != sales_invoice:
|
["sales_invoice", "item_code"], as_dict=1)
|
||||||
sales_invoice_company = frappe.db.get_value("Sales Invoice", sales_invoice, "company")
|
|
||||||
|
if not serial_no_details:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if serial_no_details.sales_invoice and serial_no_details.item_code == item.item_code \
|
||||||
|
and self.name != serial_no_details.sales_invoice:
|
||||||
|
sales_invoice_company = frappe.db.get_value("Sales Invoice", serial_no_details.sales_invoice, "company")
|
||||||
if sales_invoice_company == self.company:
|
if sales_invoice_company == self.company:
|
||||||
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
|
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
|
||||||
.format(serial_no, sales_invoice)))
|
.format(serial_no, serial_no_details.sales_invoice)))
|
||||||
|
|
||||||
def update_project(self):
|
def update_project(self):
|
||||||
if self.project:
|
if self.project:
|
||||||
@ -1231,7 +1250,8 @@ class SalesInvoice(SellingController):
|
|||||||
self.status = "Unpaid and Discounted"
|
self.status = "Unpaid and Discounted"
|
||||||
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()):
|
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()):
|
||||||
self.status = "Unpaid"
|
self.status = "Unpaid"
|
||||||
elif flt(self.outstanding_amount) < 0 and self.is_return==0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
#Check if outstanding amount is 0 due to credit note issued against invoice
|
||||||
|
elif flt(self.outstanding_amount) <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||||
self.status = "Credit Note Issued"
|
self.status = "Credit Note Issued"
|
||||||
elif self.is_return == 1:
|
elif self.is_return == 1:
|
||||||
self.status = "Return"
|
self.status = "Return"
|
||||||
|
@ -68,8 +68,6 @@
|
|||||||
"selling_price_list": "_Test Price List",
|
"selling_price_list": "_Test Price List",
|
||||||
"territory": "_Test Territory"
|
"territory": "_Test Territory"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"conversion_rate": 1.0,
|
"conversion_rate": 1.0,
|
||||||
@ -276,7 +274,6 @@
|
|||||||
"uom": "_Test UOM 1",
|
"uom": "_Test UOM 1",
|
||||||
"conversion_factor": 1,
|
"conversion_factor": 1,
|
||||||
"stock_uom": "_Test UOM 1"
|
"stock_uom": "_Test UOM 1"
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
import unittest, copy, time
|
import unittest, copy, time
|
||||||
from frappe.utils import nowdate, flt, getdate, cint
|
from frappe.utils import nowdate, flt, getdate, cint, add_days
|
||||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
|
||||||
@ -20,6 +20,9 @@ from erpnext.stock.doctype.item.test_item import create_item
|
|||||||
from six import iteritems
|
from six import iteritems
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
|
||||||
from erpnext.regional.india.utils import get_ewb_data
|
from erpnext.regional.india.utils import get_ewb_data
|
||||||
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
|
||||||
|
|
||||||
class TestSalesInvoice(unittest.TestCase):
|
class TestSalesInvoice(unittest.TestCase):
|
||||||
def make(self):
|
def make(self):
|
||||||
@ -550,7 +553,6 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.get("taxes")[6].tax_amount = 2
|
si.get("taxes")[6].tax_amount = 2
|
||||||
|
|
||||||
si.insert()
|
si.insert()
|
||||||
print(si.name)
|
|
||||||
|
|
||||||
expected_values = [
|
expected_values = [
|
||||||
{
|
{
|
||||||
@ -679,56 +681,67 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertFalse(gle)
|
self.assertFalse(gle)
|
||||||
|
|
||||||
def test_pos_gl_entry_with_perpetual_inventory(self):
|
def test_pos_gl_entry_with_perpetual_inventory(self):
|
||||||
set_perpetual_inventory()
|
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
|
|
||||||
self._insert_purchase_receipt()
|
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||||
pos = copy.deepcopy(test_records[1])
|
|
||||||
pos["is_pos"] = 1
|
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
|
||||||
pos["update_stock"] = 1
|
|
||||||
pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300},
|
pos.is_pos = 1
|
||||||
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300}]
|
pos.update_stock = 1
|
||||||
|
|
||||||
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
|
||||||
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 50})
|
||||||
|
|
||||||
|
taxes = get_taxes_and_charges()
|
||||||
|
pos.taxes = []
|
||||||
|
for tax in taxes:
|
||||||
|
pos.append("taxes", tax)
|
||||||
|
|
||||||
si = frappe.copy_doc(pos)
|
si = frappe.copy_doc(pos)
|
||||||
si.insert()
|
si.insert()
|
||||||
si.submit()
|
si.submit()
|
||||||
|
self.assertEqual(si.paid_amount, 100.0)
|
||||||
|
|
||||||
self.assertEqual(si.paid_amount, 600.0)
|
self.pos_gl_entry(si, pos, 50)
|
||||||
|
|
||||||
self.pos_gl_entry(si, pos, 300)
|
|
||||||
|
|
||||||
def test_pos_change_amount(self):
|
def test_pos_change_amount(self):
|
||||||
set_perpetual_inventory()
|
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
|
|
||||||
self._insert_purchase_receipt()
|
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||||
pos = copy.deepcopy(test_records[1])
|
|
||||||
pos["is_pos"] = 1
|
|
||||||
pos["update_stock"] = 1
|
|
||||||
pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300},
|
|
||||||
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 340}]
|
|
||||||
|
|
||||||
si = frappe.copy_doc(pos)
|
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
|
||||||
si.change_amount = 5.0
|
|
||||||
si.insert()
|
|
||||||
si.submit()
|
|
||||||
|
|
||||||
self.assertEqual(si.grand_total, 630.0)
|
pos.is_pos = 1
|
||||||
self.assertEqual(si.write_off_amount, -5)
|
pos.update_stock = 1
|
||||||
|
|
||||||
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
|
||||||
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 60})
|
||||||
|
|
||||||
|
pos.change_amount = 5.0
|
||||||
|
pos.insert()
|
||||||
|
pos.submit()
|
||||||
|
|
||||||
|
self.assertEqual(pos.grand_total, 100.0)
|
||||||
|
self.assertEqual(pos.write_off_amount, -5)
|
||||||
|
|
||||||
def test_make_pos_invoice(self):
|
def test_make_pos_invoice(self):
|
||||||
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
|
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
|
||||||
|
|
||||||
set_perpetual_inventory()
|
|
||||||
|
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
self._insert_purchase_receipt()
|
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||||
|
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
|
||||||
|
|
||||||
pos = copy.deepcopy(test_records[1])
|
pos.is_pos = 1
|
||||||
pos["is_pos"] = 1
|
pos.update_stock = 1
|
||||||
pos["update_stock"] = 1
|
|
||||||
pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300},
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
|
||||||
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}]
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 50})
|
||||||
|
|
||||||
|
taxes = get_taxes_and_charges()
|
||||||
|
pos.taxes = []
|
||||||
|
for tax in taxes:
|
||||||
|
pos.append("taxes", tax)
|
||||||
|
|
||||||
invoice_data = [{'09052016142': pos}]
|
invoice_data = [{'09052016142': pos}]
|
||||||
si = make_invoice(invoice_data).get('invoice')
|
si = make_invoice(invoice_data).get('invoice')
|
||||||
@ -736,16 +749,15 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1})
|
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1})
|
||||||
si = frappe.get_doc('Sales Invoice', sales_invoice[0].name)
|
si = frappe.get_doc('Sales Invoice', sales_invoice[0].name)
|
||||||
self.assertEqual(si.grand_total, 630.0)
|
|
||||||
|
|
||||||
self.pos_gl_entry(si, pos, 330)
|
self.assertEqual(si.grand_total, 100)
|
||||||
|
|
||||||
|
self.pos_gl_entry(si, pos, 50)
|
||||||
|
|
||||||
def test_make_pos_invoice_in_draft(self):
|
def test_make_pos_invoice_in_draft(self):
|
||||||
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
|
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
set_perpetual_inventory()
|
|
||||||
|
|
||||||
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
|
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
|
||||||
if allow_negative_stock:
|
if allow_negative_stock:
|
||||||
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
|
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
|
||||||
@ -789,7 +801,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.name, as_dict=1)[0]
|
si.name, as_dict=1)[0]
|
||||||
self.assertTrue(sle)
|
self.assertTrue(sle)
|
||||||
self.assertEqual([sle.item_code, sle.warehouse, sle.actual_qty],
|
self.assertEqual([sle.item_code, sle.warehouse, sle.actual_qty],
|
||||||
["_Test Item", "_Test Warehouse - _TC", -1.0])
|
['_Test FG Item', 'Stores - TCP1', -1.0])
|
||||||
|
|
||||||
# check gl entries
|
# check gl entries
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
@ -797,19 +809,19 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
order by account asc, debit asc, credit asc""", si.name, as_dict=1)
|
order by account asc, debit asc, credit asc""", si.name, as_dict=1)
|
||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
stock_in_hand = get_inventory_account('_Test Company')
|
stock_in_hand = get_inventory_account('_Test Company with perpetual inventory')
|
||||||
|
|
||||||
expected_gl_entries = sorted([
|
expected_gl_entries = sorted([
|
||||||
[si.debit_to, 630.0, 0.0],
|
[si.debit_to, 100.0, 0.0],
|
||||||
[pos["items"][0]["income_account"], 0.0, 500.0],
|
[pos.items[0].income_account, 0.0, 89.09],
|
||||||
[pos["taxes"][0]["account_head"], 0.0, 80.0],
|
['Round Off - TCP1', 0.0, 0.01],
|
||||||
[pos["taxes"][1]["account_head"], 0.0, 50.0],
|
[pos.taxes[0].account_head, 0.0, 10.69],
|
||||||
|
[pos.taxes[1].account_head, 0.0, 0.21],
|
||||||
[stock_in_hand, 0.0, abs(sle.stock_value_difference)],
|
[stock_in_hand, 0.0, abs(sle.stock_value_difference)],
|
||||||
[pos["items"][0]["expense_account"], abs(sle.stock_value_difference), 0.0],
|
[pos.items[0].expense_account, abs(sle.stock_value_difference), 0.0],
|
||||||
[si.debit_to, 0.0, 300.0],
|
[si.debit_to, 0.0, 50.0],
|
||||||
[si.debit_to, 0.0, cash_amount],
|
[si.debit_to, 0.0, cash_amount],
|
||||||
["_Test Bank - _TC", 300.0, 0.0],
|
["_Test Bank - TCP1", 50, 0.0],
|
||||||
["Cash - _TC", cash_amount, 0.0]
|
["Cash - TCP1", cash_amount, 0.0]
|
||||||
])
|
])
|
||||||
|
|
||||||
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
|
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
|
||||||
@ -823,9 +835,9 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertFalse(gle)
|
self.assertFalse(gle)
|
||||||
|
|
||||||
set_perpetual_inventory(0)
|
|
||||||
|
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
si.delete()
|
||||||
|
|
||||||
def test_pos_si_without_payment(self):
|
def test_pos_si_without_payment(self):
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
@ -1008,7 +1020,6 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
|
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
se = make_serialized_item()
|
se = make_serialized_item()
|
||||||
@ -1023,14 +1034,17 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(si.get("items")[0].serial_no, dn.get("items")[0].serial_no)
|
self.assertEqual(si.get("items")[0].serial_no, dn.get("items")[0].serial_no)
|
||||||
|
|
||||||
def test_return_sales_invoice(self):
|
def test_return_sales_invoice(self):
|
||||||
set_perpetual_inventory()
|
make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
|
||||||
|
|
||||||
actual_qty_0 = get_qty_after_transaction()
|
actual_qty_0 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
|
||||||
|
|
||||||
si = create_sales_invoice(qty=5, rate=500, update_stock=1)
|
si = create_sales_invoice(qty = 5, rate=500, update_stock=1, company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1")
|
||||||
|
|
||||||
|
|
||||||
|
actual_qty_1 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
|
||||||
|
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
actual_qty_1 = get_qty_after_transaction()
|
|
||||||
self.assertEqual(actual_qty_0 - 5, actual_qty_1)
|
self.assertEqual(actual_qty_0 - 5, actual_qty_1)
|
||||||
|
|
||||||
# outgoing_rate
|
# outgoing_rate
|
||||||
@ -1038,10 +1052,9 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"voucher_no": si.name}, "stock_value_difference") / 5
|
"voucher_no": si.name}, "stock_value_difference") / 5
|
||||||
|
|
||||||
# return entry
|
# return entry
|
||||||
si1 = create_sales_invoice(is_return=1, return_against=si.name, qty=-2, rate=500, update_stock=1)
|
si1 = create_sales_invoice(is_return=1, return_against=si.name, qty=-2, rate=500, update_stock=1, company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1")
|
||||||
|
|
||||||
actual_qty_2 = get_qty_after_transaction()
|
|
||||||
|
|
||||||
|
actual_qty_2 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
|
||||||
self.assertEqual(actual_qty_1 + 2, actual_qty_2)
|
self.assertEqual(actual_qty_1 + 2, actual_qty_2)
|
||||||
|
|
||||||
incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
|
incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
|
||||||
@ -1049,7 +1062,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
["incoming_rate", "stock_value_difference"])
|
["incoming_rate", "stock_value_difference"])
|
||||||
|
|
||||||
self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
|
self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
|
||||||
stock_in_hand_account = get_inventory_account('_Test Company', si1.items[0].warehouse)
|
stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory', si1.items[0].warehouse)
|
||||||
|
|
||||||
# Check gl entry
|
# Check gl entry
|
||||||
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
|
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
|
||||||
@ -1058,7 +1071,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(gle_warehouse_amount, stock_value_difference)
|
self.assertEqual(gle_warehouse_amount, stock_value_difference)
|
||||||
|
|
||||||
party_credited = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
|
party_credited = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
|
||||||
"voucher_no": si1.name, "account": "Debtors - _TC", "party": "_Test Customer"}, "credit")
|
"voucher_no": si1.name, "account": "Debtors - TCP1", "party": "_Test Customer"}, "credit")
|
||||||
|
|
||||||
self.assertEqual(party_credited, 1000)
|
self.assertEqual(party_credited, 1000)
|
||||||
|
|
||||||
@ -1066,7 +1079,6 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertFalse(si1.outstanding_amount)
|
self.assertFalse(si1.outstanding_amount)
|
||||||
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
|
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
|
||||||
|
|
||||||
set_perpetual_inventory(0)
|
|
||||||
|
|
||||||
def test_discount_on_net_total(self):
|
def test_discount_on_net_total(self):
|
||||||
si = frappe.copy_doc(test_records[2])
|
si = frappe.copy_doc(test_records[2])
|
||||||
@ -1524,6 +1536,8 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(si.total_taxes_and_charges, 577.05)
|
self.assertEqual(si.total_taxes_and_charges, 577.05)
|
||||||
self.assertEqual(si.grand_total, 1827.05)
|
self.assertEqual(si.grand_total, 1827.05)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_invoice_without_terms(self):
|
def test_create_invoice_without_terms(self):
|
||||||
si = create_sales_invoice(do_not_save=1)
|
si = create_sales_invoice(do_not_save=1)
|
||||||
self.assertFalse(si.get('payment_schedule'))
|
self.assertFalse(si.get('payment_schedule'))
|
||||||
@ -1833,6 +1847,26 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
|
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
|
||||||
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
|
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
|
||||||
|
|
||||||
|
def test_item_tax_validity(self):
|
||||||
|
item = frappe.get_doc("Item", "_Test Item 2")
|
||||||
|
|
||||||
|
if item.taxes:
|
||||||
|
item.taxes = []
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
item.append("taxes", {
|
||||||
|
"item_tax_template": "_Test Item Tax Template 1",
|
||||||
|
"valid_from": add_days(nowdate(), 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1)
|
||||||
|
sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1"
|
||||||
|
self.assertRaises(frappe.ValidationError, sales_invoice.save)
|
||||||
|
|
||||||
|
item.taxes = []
|
||||||
|
item.save()
|
||||||
|
|
||||||
def create_sales_invoice(**args):
|
def create_sales_invoice(**args):
|
||||||
si = frappe.new_doc("Sales Invoice")
|
si = frappe.new_doc("Sales Invoice")
|
||||||
@ -1930,4 +1964,29 @@ def get_outstanding_amount(against_voucher_type, against_voucher, account, party
|
|||||||
if against_voucher_type == 'Purchase Invoice':
|
if against_voucher_type == 'Purchase Invoice':
|
||||||
bal = bal * -1
|
bal = bal * -1
|
||||||
|
|
||||||
return bal
|
return bal
|
||||||
|
|
||||||
|
def get_taxes_and_charges():
|
||||||
|
return [{
|
||||||
|
"account_head": "_Test Account Excise Duty - TCP1",
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"cost_center": "Main - TCP1",
|
||||||
|
"description": "Excise Duty",
|
||||||
|
"doctype": "Sales Taxes and Charges",
|
||||||
|
"idx": 1,
|
||||||
|
"included_in_print_rate": 1,
|
||||||
|
"parentfield": "taxes",
|
||||||
|
"rate": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"account_head": "_Test Account Education Cess - TCP1",
|
||||||
|
"charge_type": "On Previous Row Amount",
|
||||||
|
"cost_center": "Main - TCP1",
|
||||||
|
"description": "Education Cess",
|
||||||
|
"doctype": "Sales Taxes and Charges",
|
||||||
|
"idx": 2,
|
||||||
|
"included_in_print_rate": 1,
|
||||||
|
"parentfield": "taxes",
|
||||||
|
"rate": 2,
|
||||||
|
"row_id": 1
|
||||||
|
}]
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-06-04 11:02:19",
|
"creation": "2013-06-04 11:02:19",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -484,7 +485,8 @@
|
|||||||
"depends_on": "enable_deferred_revenue",
|
"depends_on": "enable_deferred_revenue",
|
||||||
"fieldname": "service_stop_date",
|
"fieldname": "service_stop_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service Stop Date"
|
"label": "Service Stop Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -500,13 +502,15 @@
|
|||||||
"depends_on": "enable_deferred_revenue",
|
"depends_on": "enable_deferred_revenue",
|
||||||
"fieldname": "service_start_date",
|
"fieldname": "service_start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service Start Date"
|
"label": "Service Start Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "enable_deferred_revenue",
|
"depends_on": "enable_deferred_revenue",
|
||||||
"fieldname": "service_end_date",
|
"fieldname": "service_end_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Service End Date"
|
"label": "Service End Date",
|
||||||
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
@ -783,7 +787,8 @@
|
|||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-07-16 16:36:46.527606",
|
"links": [],
|
||||||
|
"modified": "2019-12-04 12:22:38.517710",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
|
@ -1,299 +1,119 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"allow_rename": 1,
|
||||||
"allow_rename": 1,
|
"creation": "2013-01-10 16:34:09",
|
||||||
"autoname": "field:title",
|
"description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.",
|
||||||
"beta": 0,
|
"doctype": "DocType",
|
||||||
"creation": "2013-01-10 16:34:09",
|
"document_type": "Setup",
|
||||||
"custom": 0,
|
"engine": "InnoDB",
|
||||||
"description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.",
|
"field_order": [
|
||||||
"docstatus": 0,
|
"title",
|
||||||
"doctype": "DocType",
|
"is_default",
|
||||||
"document_type": "Setup",
|
"disabled",
|
||||||
"editable_grid": 0,
|
"column_break_3",
|
||||||
|
"company",
|
||||||
|
"tax_category",
|
||||||
|
"section_break_5",
|
||||||
|
"taxes"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "title",
|
||||||
"bold": 0,
|
"fieldtype": "Data",
|
||||||
"collapsible": 0,
|
"label": "Title",
|
||||||
"columns": 0,
|
"no_copy": 1,
|
||||||
"fieldname": "title",
|
"oldfieldname": "title",
|
||||||
"fieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"hidden": 0,
|
"reqd": 1
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "title",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "is_default",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "is_default",
|
"label": "Default"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Default",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "disabled",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"label": "Disabled"
|
||||||
"fieldname": "disabled",
|
},
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Disabled",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "column_break_3",
|
||||||
"bold": 0,
|
"fieldtype": "Column Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_3",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "company",
|
||||||
"bold": 0,
|
"fieldtype": "Link",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"columns": 0,
|
"in_standard_filter": 1,
|
||||||
"fieldname": "company",
|
"label": "Company",
|
||||||
"fieldtype": "Link",
|
"oldfieldname": "company",
|
||||||
"hidden": 0,
|
"oldfieldtype": "Link",
|
||||||
"ignore_user_permissions": 0,
|
"options": "Company",
|
||||||
"ignore_xss_filter": 0,
|
"remember_last_selected_value": 1,
|
||||||
"in_filter": 1,
|
"reqd": 1
|
||||||
"in_list_view": 1,
|
},
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "company",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "section_break_5",
|
||||||
"bold": 0,
|
"fieldtype": "Section Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_5",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"description": "* Will be calculated in the transaction.",
|
||||||
"bold": 0,
|
"fieldname": "taxes",
|
||||||
"collapsible": 0,
|
"fieldtype": "Table",
|
||||||
"columns": 0,
|
"label": "Sales Taxes and Charges",
|
||||||
"description": "* Will be calculated in the transaction.",
|
"oldfieldname": "other_charges",
|
||||||
"fieldname": "taxes",
|
"oldfieldtype": "Table",
|
||||||
"fieldtype": "Table",
|
"options": "Sales Taxes and Charges"
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "tax_category",
|
||||||
"in_filter": 0,
|
"fieldtype": "Link",
|
||||||
"in_list_view": 0,
|
"label": "Tax Category",
|
||||||
"in_standard_filter": 0,
|
"options": "Tax Category"
|
||||||
"label": "Sales Taxes and Charges",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "other_charges",
|
|
||||||
"oldfieldtype": "Table",
|
|
||||||
"options": "Sales Taxes and Charges",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"icon": "fa fa-money",
|
||||||
"hide_toolbar": 0,
|
"idx": 1,
|
||||||
"icon": "fa fa-money",
|
"modified": "2019-11-25 13:06:03.279099",
|
||||||
"idx": 1,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "Sales Taxes and Charges Template",
|
||||||
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2016-11-07 05:18:41.743257",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Sales Taxes and Charges Template",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 1,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"report": 1,
|
||||||
"delete": 0,
|
"role": "Sales User"
|
||||||
"email": 1,
|
},
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Sales User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Accounts Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Sales Master Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Sales Master Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "ASC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"sort_order": "ASC",
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -21,6 +21,8 @@ frappe.ui.form.on('Share Transfer', {
|
|||||||
erpnext.share_transfer.make_jv(frm);
|
erpnext.share_transfer.make_jv(frm);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frm.toggle_reqd("asset_account", frm.doc.transfer_type != "Transfer");
|
||||||
},
|
},
|
||||||
no_of_shares: (frm) => {
|
no_of_shares: (frm) => {
|
||||||
if (frm.doc.rate != undefined || frm.doc.rate != null){
|
if (frm.doc.rate != undefined || frm.doc.rate != null){
|
||||||
@ -56,6 +58,10 @@ frappe.ui.form.on('Share Transfer', {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
transfer_type: function(frm) {
|
||||||
|
frm.toggle_reqd("asset_account", frm.doc.transfer_type != "Transfer");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,9 +13,9 @@ from frappe.utils import nowdate
|
|||||||
class ShareDontExists(ValidationError): pass
|
class ShareDontExists(ValidationError): pass
|
||||||
|
|
||||||
class ShareTransfer(Document):
|
class ShareTransfer(Document):
|
||||||
def before_submit(self):
|
def on_submit(self):
|
||||||
if self.transfer_type == 'Issue':
|
if self.transfer_type == 'Issue':
|
||||||
shareholder = self.get_shareholder_doc(self.company)
|
shareholder = self.get_company_shareholder()
|
||||||
shareholder.append('share_balance', {
|
shareholder.append('share_balance', {
|
||||||
'share_type': self.share_type,
|
'share_type': self.share_type,
|
||||||
'from_no': self.from_no,
|
'from_no': self.from_no,
|
||||||
@ -28,7 +28,7 @@ class ShareTransfer(Document):
|
|||||||
})
|
})
|
||||||
shareholder.save()
|
shareholder.save()
|
||||||
|
|
||||||
doc = frappe.get_doc('Shareholder', self.to_shareholder)
|
doc = self.get_shareholder_doc(self.to_shareholder)
|
||||||
doc.append('share_balance', {
|
doc.append('share_balance', {
|
||||||
'share_type': self.share_type,
|
'share_type': self.share_type,
|
||||||
'from_no': self.from_no,
|
'from_no': self.from_no,
|
||||||
@ -41,11 +41,11 @@ class ShareTransfer(Document):
|
|||||||
|
|
||||||
elif self.transfer_type == 'Purchase':
|
elif self.transfer_type == 'Purchase':
|
||||||
self.remove_shares(self.from_shareholder)
|
self.remove_shares(self.from_shareholder)
|
||||||
self.remove_shares(self.get_shareholder_doc(self.company).name)
|
self.remove_shares(self.get_company_shareholder().name)
|
||||||
|
|
||||||
elif self.transfer_type == 'Transfer':
|
elif self.transfer_type == 'Transfer':
|
||||||
self.remove_shares(self.from_shareholder)
|
self.remove_shares(self.from_shareholder)
|
||||||
doc = frappe.get_doc('Shareholder', self.to_shareholder)
|
doc = self.get_shareholder_doc(self.to_shareholder)
|
||||||
doc.append('share_balance', {
|
doc.append('share_balance', {
|
||||||
'share_type': self.share_type,
|
'share_type': self.share_type,
|
||||||
'from_no': self.from_no,
|
'from_no': self.from_no,
|
||||||
@ -56,143 +56,127 @@ class ShareTransfer(Document):
|
|||||||
})
|
})
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
|
def on_cancel(self):
|
||||||
|
if self.transfer_type == 'Issue':
|
||||||
|
compnay_shareholder = self.get_company_shareholder()
|
||||||
|
self.remove_shares(compnay_shareholder.name)
|
||||||
|
self.remove_shares(self.to_shareholder)
|
||||||
|
|
||||||
|
elif self.transfer_type == 'Purchase':
|
||||||
|
compnay_shareholder = self.get_company_shareholder()
|
||||||
|
from_shareholder = self.get_shareholder_doc(self.from_shareholder)
|
||||||
|
|
||||||
|
from_shareholder.append('share_balance', {
|
||||||
|
'share_type': self.share_type,
|
||||||
|
'from_no': self.from_no,
|
||||||
|
'to_no': self.to_no,
|
||||||
|
'rate': self.rate,
|
||||||
|
'amount': self.amount,
|
||||||
|
'no_of_shares': self.no_of_shares
|
||||||
|
})
|
||||||
|
|
||||||
|
from_shareholder.save()
|
||||||
|
|
||||||
|
compnay_shareholder.append('share_balance', {
|
||||||
|
'share_type': self.share_type,
|
||||||
|
'from_no': self.from_no,
|
||||||
|
'to_no': self.to_no,
|
||||||
|
'rate': self.rate,
|
||||||
|
'amount': self.amount,
|
||||||
|
'no_of_shares': self.no_of_shares
|
||||||
|
})
|
||||||
|
|
||||||
|
compnay_shareholder.save()
|
||||||
|
|
||||||
|
elif self.transfer_type == 'Transfer':
|
||||||
|
self.remove_shares(self.to_shareholder)
|
||||||
|
from_shareholder = self.get_shareholder_doc(self.from_shareholder)
|
||||||
|
from_shareholder.append('share_balance', {
|
||||||
|
'share_type': self.share_type,
|
||||||
|
'from_no': self.from_no,
|
||||||
|
'to_no': self.to_no,
|
||||||
|
'rate': self.rate,
|
||||||
|
'amount': self.amount,
|
||||||
|
'no_of_shares': self.no_of_shares
|
||||||
|
})
|
||||||
|
from_shareholder.save()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.get_company_shareholder()
|
||||||
self.basic_validations()
|
self.basic_validations()
|
||||||
self.folio_no_validation()
|
self.folio_no_validation()
|
||||||
|
|
||||||
if self.transfer_type == 'Issue':
|
if self.transfer_type == 'Issue':
|
||||||
if not self.get_shareholder_doc(self.company):
|
# validate share doesn't exist in company
|
||||||
shareholder = frappe.get_doc({
|
ret_val = self.share_exists(self.get_company_shareholder().name)
|
||||||
'doctype': 'Shareholder',
|
if ret_val in ('Complete', 'Partial'):
|
||||||
'title': self.company,
|
|
||||||
'company': self.company,
|
|
||||||
'is_company': 1
|
|
||||||
})
|
|
||||||
shareholder.insert()
|
|
||||||
# validate share doesnt exist in company
|
|
||||||
ret_val = self.share_exists(self.get_shareholder_doc(self.company).name)
|
|
||||||
if ret_val != False:
|
|
||||||
frappe.throw(_('The shares already exist'), frappe.DuplicateEntryError)
|
frappe.throw(_('The shares already exist'), frappe.DuplicateEntryError)
|
||||||
else:
|
else:
|
||||||
# validate share exists with from_shareholder
|
# validate share exists with from_shareholder
|
||||||
ret_val = self.share_exists(self.from_shareholder)
|
ret_val = self.share_exists(self.from_shareholder)
|
||||||
if ret_val != True:
|
if ret_val in ('Outside', 'Partial'):
|
||||||
frappe.throw(_("The shares don't exist with the {0}")
|
frappe.throw(_("The shares don't exist with the {0}")
|
||||||
.format(self.from_shareholder), ShareDontExists)
|
.format(self.from_shareholder), ShareDontExists)
|
||||||
|
|
||||||
def basic_validations(self):
|
def basic_validations(self):
|
||||||
if self.transfer_type == 'Purchase':
|
if self.transfer_type == 'Purchase':
|
||||||
self.to_shareholder = ''
|
self.to_shareholder = ''
|
||||||
if self.from_shareholder is None or self.from_shareholder is '':
|
if not self.from_shareholder:
|
||||||
frappe.throw(_('The field From Shareholder cannot be blank'))
|
frappe.throw(_('The field From Shareholder cannot be blank'))
|
||||||
if self.from_folio_no is None or self.from_folio_no is '':
|
if not self.from_folio_no:
|
||||||
self.to_folio_no = self.autoname_folio(self.to_shareholder)
|
self.to_folio_no = self.autoname_folio(self.to_shareholder)
|
||||||
if self.asset_account is None:
|
if not self.asset_account:
|
||||||
frappe.throw(_('The field Asset Account cannot be blank'))
|
frappe.throw(_('The field Asset Account cannot be blank'))
|
||||||
elif (self.transfer_type == 'Issue'):
|
elif (self.transfer_type == 'Issue'):
|
||||||
self.from_shareholder = ''
|
self.from_shareholder = ''
|
||||||
if self.to_shareholder is None or self.to_shareholder == '':
|
if not self.to_shareholder:
|
||||||
frappe.throw(_('The field To Shareholder cannot be blank'))
|
frappe.throw(_('The field To Shareholder cannot be blank'))
|
||||||
if self.to_folio_no is None or self.to_folio_no is '':
|
if not self.to_folio_no:
|
||||||
self.to_folio_no = self.autoname_folio(self.to_shareholder)
|
self.to_folio_no = self.autoname_folio(self.to_shareholder)
|
||||||
if self.asset_account is None:
|
if not self.asset_account:
|
||||||
frappe.throw(_('The field Asset Account cannot be blank'))
|
frappe.throw(_('The field Asset Account cannot be blank'))
|
||||||
else:
|
else:
|
||||||
if self.from_shareholder is None or self.to_shareholder is None:
|
if not self.from_shareholder or not self.to_shareholder:
|
||||||
frappe.throw(_('The fields From Shareholder and To Shareholder cannot be blank'))
|
frappe.throw(_('The fields From Shareholder and To Shareholder cannot be blank'))
|
||||||
if self.to_folio_no is None or self.to_folio_no is '':
|
if not self.to_folio_no:
|
||||||
self.to_folio_no = self.autoname_folio(self.to_shareholder)
|
self.to_folio_no = self.autoname_folio(self.to_shareholder)
|
||||||
if self.equity_or_liability_account is None:
|
if not self.equity_or_liability_account:
|
||||||
frappe.throw(_('The field Equity/Liability Account cannot be blank'))
|
frappe.throw(_('The field Equity/Liability Account cannot be blank'))
|
||||||
if self.from_shareholder == self.to_shareholder:
|
if self.from_shareholder == self.to_shareholder:
|
||||||
frappe.throw(_('The seller and the buyer cannot be the same'))
|
frappe.throw(_('The seller and the buyer cannot be the same'))
|
||||||
if self.no_of_shares != self.to_no - self.from_no + 1:
|
if self.no_of_shares != self.to_no - self.from_no + 1:
|
||||||
frappe.throw(_('The number of shares and the share numbers are inconsistent'))
|
frappe.throw(_('The number of shares and the share numbers are inconsistent'))
|
||||||
if self.amount is None:
|
if not self.amount:
|
||||||
self.amount = self.rate * self.no_of_shares
|
self.amount = self.rate * self.no_of_shares
|
||||||
if self.amount != self.rate * self.no_of_shares:
|
if self.amount != self.rate * self.no_of_shares:
|
||||||
frappe.throw(_('There are inconsistencies between the rate, no of shares and the amount calculated'))
|
frappe.throw(_('There are inconsistencies between the rate, no of shares and the amount calculated'))
|
||||||
|
|
||||||
def share_exists(self, shareholder):
|
def share_exists(self, shareholder):
|
||||||
# return True if exits,
|
doc = self.get_shareholder_doc(shareholder)
|
||||||
# False if completely doesn't exist,
|
|
||||||
# 'partially exists' if partailly doesn't exist
|
|
||||||
ret_val = self.recursive_share_check(shareholder, self.share_type,
|
|
||||||
query = {
|
|
||||||
'from_no': self.from_no,
|
|
||||||
'to_no': self.to_no
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if all(boolean == True for boolean in ret_val):
|
|
||||||
return True
|
|
||||||
elif True in ret_val:
|
|
||||||
return 'partially exists'
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def recursive_share_check(self, shareholder, share_type, query):
|
|
||||||
# query = {'from_no': share_starting_no, 'to_no': share_ending_no}
|
|
||||||
# Recursive check if a given part of shares is held by the shareholder
|
|
||||||
# return a list containing True and False
|
|
||||||
# Eg. [True, False, True]
|
|
||||||
# All True implies its completely inside
|
|
||||||
# All False implies its completely outside
|
|
||||||
# A mix implies its partially inside/outside
|
|
||||||
does_share_exist = []
|
|
||||||
doc = frappe.get_doc('Shareholder', shareholder)
|
|
||||||
for entry in doc.share_balance:
|
for entry in doc.share_balance:
|
||||||
if entry.share_type != share_type or \
|
if entry.share_type != self.share_type or \
|
||||||
entry.from_no > query['to_no'] or \
|
entry.from_no > self.to_no or \
|
||||||
entry.to_no < query['from_no']:
|
entry.to_no < self.from_no:
|
||||||
continue # since query lies outside bounds
|
continue # since query lies outside bounds
|
||||||
elif entry.from_no <= query['from_no'] and entry.to_no >= query['to_no']:
|
elif entry.from_no <= self.from_no and entry.to_no >= self.to_no: #both inside
|
||||||
return [True] # absolute truth!
|
return 'Complete' # absolute truth!
|
||||||
elif entry.from_no >= query['from_no'] and entry.to_no <= query['to_no']:
|
elif entry.from_no <= self.from_no <= self.to_no:
|
||||||
# split and check
|
return 'Partial'
|
||||||
does_share_exist.extend(self.recursive_share_check(shareholder,
|
elif entry.from_no <= self.to_no <= entry.to_no:
|
||||||
share_type,
|
return 'Partial'
|
||||||
{
|
|
||||||
'from_no': query['from_no'],
|
|
||||||
'to_no': entry.from_no - 1
|
|
||||||
}
|
|
||||||
))
|
|
||||||
does_share_exist.append(True)
|
|
||||||
does_share_exist.extend(self.recursive_share_check(shareholder,
|
|
||||||
share_type,
|
|
||||||
{
|
|
||||||
'from_no': entry.to_no + 1,
|
|
||||||
'to_no': query['to_no']
|
|
||||||
}
|
|
||||||
))
|
|
||||||
elif query['from_no'] <= entry.from_no <= query['to_no'] and entry.to_no >= query['to_no']:
|
|
||||||
does_share_exist.extend(self.recursive_share_check(shareholder,
|
|
||||||
share_type,
|
|
||||||
{
|
|
||||||
'from_no': query['from_no'],
|
|
||||||
'to_no': entry.from_no - 1
|
|
||||||
}
|
|
||||||
))
|
|
||||||
elif query['from_no'] <= entry.to_no <= query['to_no'] and entry.from_no <= query['from_no']:
|
|
||||||
does_share_exist.extend(self.recursive_share_check(shareholder,
|
|
||||||
share_type,
|
|
||||||
{
|
|
||||||
'from_no': entry.to_no + 1,
|
|
||||||
'to_no': query['to_no']
|
|
||||||
}
|
|
||||||
))
|
|
||||||
|
|
||||||
does_share_exist.append(False)
|
return 'Outside'
|
||||||
return does_share_exist
|
|
||||||
|
|
||||||
def folio_no_validation(self):
|
def folio_no_validation(self):
|
||||||
shareholders = ['from_shareholder', 'to_shareholder']
|
shareholders = ['from_shareholder', 'to_shareholder']
|
||||||
shareholders = [shareholder for shareholder in shareholders if self.get(shareholder) is not '']
|
shareholders = [shareholder for shareholder in shareholders if self.get(shareholder) is not '']
|
||||||
for shareholder in shareholders:
|
for shareholder in shareholders:
|
||||||
doc = frappe.get_doc('Shareholder', self.get(shareholder))
|
doc = self.get_shareholder_doc(self.get(shareholder))
|
||||||
if doc.company != self.company:
|
if doc.company != self.company:
|
||||||
frappe.throw(_('The shareholder does not belong to this company'))
|
frappe.throw(_('The shareholder does not belong to this company'))
|
||||||
if doc.folio_no is '' or doc.folio_no is None:
|
if not doc.folio_no:
|
||||||
doc.folio_no = self.from_folio_no \
|
doc.folio_no = self.from_folio_no \
|
||||||
if (shareholder == 'from_shareholder') else self.to_folio_no;
|
if (shareholder == 'from_shareholder') else self.to_folio_no
|
||||||
doc.save()
|
doc.save()
|
||||||
else:
|
else:
|
||||||
if doc.folio_no and doc.folio_no != (self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no):
|
if doc.folio_no and doc.folio_no != (self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no):
|
||||||
@ -200,24 +184,14 @@ class ShareTransfer(Document):
|
|||||||
|
|
||||||
def autoname_folio(self, shareholder, is_company=False):
|
def autoname_folio(self, shareholder, is_company=False):
|
||||||
if is_company:
|
if is_company:
|
||||||
doc = self.get_shareholder_doc(shareholder)
|
doc = self.get_company_shareholder()
|
||||||
else:
|
else:
|
||||||
doc = frappe.get_doc('Shareholder' , shareholder)
|
doc = self.get_shareholder_doc(shareholder)
|
||||||
doc.folio_no = make_autoname('FN.#####')
|
doc.folio_no = make_autoname('FN.#####')
|
||||||
doc.save()
|
doc.save()
|
||||||
return doc.folio_no
|
return doc.folio_no
|
||||||
|
|
||||||
def remove_shares(self, shareholder):
|
def remove_shares(self, shareholder):
|
||||||
self.iterative_share_removal(shareholder, self.share_type,
|
|
||||||
{
|
|
||||||
'from_no': self.from_no,
|
|
||||||
'to_no' : self.to_no
|
|
||||||
},
|
|
||||||
rate = self.rate,
|
|
||||||
amount = self.amount
|
|
||||||
)
|
|
||||||
|
|
||||||
def iterative_share_removal(self, shareholder, share_type, query, rate, amount):
|
|
||||||
# query = {'from_no': share_starting_no, 'to_no': share_ending_no}
|
# query = {'from_no': share_starting_no, 'to_no': share_ending_no}
|
||||||
# Shares exist for sure
|
# Shares exist for sure
|
||||||
# Iterate over all entries and modify entry if in entry
|
# Iterate over all entries and modify entry if in entry
|
||||||
@ -227,31 +201,31 @@ class ShareTransfer(Document):
|
|||||||
|
|
||||||
for entry in current_entries:
|
for entry in current_entries:
|
||||||
# use spaceage logic here
|
# use spaceage logic here
|
||||||
if entry.share_type != share_type or \
|
if entry.share_type != self.share_type or \
|
||||||
entry.from_no > query['to_no'] or \
|
entry.from_no > self.to_no or \
|
||||||
entry.to_no < query['from_no']:
|
entry.to_no < self.from_no:
|
||||||
new_entries.append(entry)
|
new_entries.append(entry)
|
||||||
continue # since query lies outside bounds
|
continue # since query lies outside bounds
|
||||||
elif entry.from_no <= query['from_no'] and entry.to_no >= query['to_no']:
|
elif entry.from_no <= self.from_no and entry.to_no >= self.to_no:
|
||||||
#split
|
#split
|
||||||
if entry.from_no == query['from_no']:
|
if entry.from_no == self.from_no:
|
||||||
if entry.to_no == query['to_no']:
|
if entry.to_no == self.to_no:
|
||||||
pass #nothing to append
|
pass #nothing to append
|
||||||
else:
|
else:
|
||||||
new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
|
new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate))
|
||||||
else:
|
else:
|
||||||
if entry.to_no == query['to_no']:
|
if entry.to_no == self.to_no:
|
||||||
new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
|
new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate))
|
||||||
else:
|
else:
|
||||||
new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
|
new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate))
|
||||||
new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
|
new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate))
|
||||||
elif entry.from_no >= query['from_no'] and entry.to_no <= query['to_no']:
|
elif entry.from_no >= self.from_no and entry.to_no <= self.to_no:
|
||||||
# split and check
|
# split and check
|
||||||
pass #nothing to append
|
pass #nothing to append
|
||||||
elif query['from_no'] <= entry.from_no <= query['to_no'] and entry.to_no >= query['to_no']:
|
elif self.from_no <= entry.from_no <= self.to_no and entry.to_no >= self.to_no:
|
||||||
new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
|
new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate))
|
||||||
elif query['from_no'] <= entry.to_no <= query['to_no'] and entry.from_no <= query['from_no']:
|
elif self.from_no <= entry.to_no <= self.to_no and entry.from_no <= self.from_no:
|
||||||
new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
|
new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate))
|
||||||
else:
|
else:
|
||||||
new_entries.append(entry)
|
new_entries.append(entry)
|
||||||
|
|
||||||
@ -272,16 +246,34 @@ class ShareTransfer(Document):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_shareholder_doc(self, shareholder):
|
def get_shareholder_doc(self, shareholder):
|
||||||
# Get Shareholder doc based on the Shareholder title
|
# Get Shareholder doc based on the Shareholder name
|
||||||
doc = frappe.get_list('Shareholder',
|
if shareholder:
|
||||||
filters = [
|
query_filters = {'name': shareholder}
|
||||||
('Shareholder', 'title', '=', shareholder)
|
|
||||||
]
|
name = frappe.db.get_value('Shareholder', {'name': shareholder}, 'name')
|
||||||
)
|
|
||||||
if len(doc) == 1:
|
return frappe.get_doc('Shareholder', name)
|
||||||
return frappe.get_doc('Shareholder', doc[0]['name'])
|
|
||||||
else: #It will necessarily by 0 indicating it doesn't exist
|
def get_company_shareholder(self):
|
||||||
return False
|
# Get company doc or create one if not present
|
||||||
|
company_shareholder = frappe.db.get_value('Shareholder',
|
||||||
|
{
|
||||||
|
'company': self.company,
|
||||||
|
'is_company': 1
|
||||||
|
}, 'name')
|
||||||
|
|
||||||
|
if company_shareholder:
|
||||||
|
return frappe.get_doc('Shareholder', company_shareholder)
|
||||||
|
else:
|
||||||
|
shareholder = frappe.get_doc({
|
||||||
|
'doctype': 'Shareholder',
|
||||||
|
'title': self.company,
|
||||||
|
'company': self.company,
|
||||||
|
'is_company': 1
|
||||||
|
})
|
||||||
|
shareholder.insert()
|
||||||
|
|
||||||
|
return shareholder
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_jv_entry( company, account, amount, payment_account,\
|
def make_jv_entry( company, account, amount, payment_account,\
|
||||||
|
@ -15,73 +15,73 @@ class TestShareTransfer(unittest.TestCase):
|
|||||||
frappe.db.sql("delete from `tabShare Balance`")
|
frappe.db.sql("delete from `tabShare Balance`")
|
||||||
share_transfers = [
|
share_transfers = [
|
||||||
{
|
{
|
||||||
"doctype" : "Share Transfer",
|
"doctype": "Share Transfer",
|
||||||
"transfer_type" : "Issue",
|
"transfer_type": "Issue",
|
||||||
"date" : "2018-01-01",
|
"date": "2018-01-01",
|
||||||
"to_shareholder" : "SH-00001",
|
"to_shareholder": "SH-00001",
|
||||||
"share_type" : "Equity",
|
"share_type": "Equity",
|
||||||
"from_no" : 1,
|
"from_no": 1,
|
||||||
"to_no" : 500,
|
"to_no": 500,
|
||||||
"no_of_shares" : 500,
|
"no_of_shares": 500,
|
||||||
"rate" : 10,
|
"rate": 10,
|
||||||
"company" : "_Test Company",
|
"company": "_Test Company",
|
||||||
"asset_account" : "Cash - _TC",
|
"asset_account": "Cash - _TC",
|
||||||
"equity_or_liability_account": "Creditors - _TC"
|
"equity_or_liability_account": "Creditors - _TC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype" : "Share Transfer",
|
"doctype": "Share Transfer",
|
||||||
"transfer_type" : "Transfer",
|
"transfer_type": "Transfer",
|
||||||
"date" : "2018-01-02",
|
"date": "2018-01-02",
|
||||||
"from_shareholder" : "SH-00001",
|
"from_shareholder": "SH-00001",
|
||||||
"to_shareholder" : "SH-00002",
|
"to_shareholder": "SH-00002",
|
||||||
"share_type" : "Equity",
|
"share_type": "Equity",
|
||||||
"from_no" : 101,
|
"from_no": 101,
|
||||||
"to_no" : 200,
|
"to_no": 200,
|
||||||
"no_of_shares" : 100,
|
"no_of_shares": 100,
|
||||||
"rate" : 15,
|
"rate": 15,
|
||||||
"company" : "_Test Company",
|
"company": "_Test Company",
|
||||||
"equity_or_liability_account": "Creditors - _TC"
|
"equity_or_liability_account": "Creditors - _TC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype" : "Share Transfer",
|
"doctype": "Share Transfer",
|
||||||
"transfer_type" : "Transfer",
|
"transfer_type": "Transfer",
|
||||||
"date" : "2018-01-03",
|
"date": "2018-01-03",
|
||||||
"from_shareholder" : "SH-00001",
|
"from_shareholder": "SH-00001",
|
||||||
"to_shareholder" : "SH-00003",
|
"to_shareholder": "SH-00003",
|
||||||
"share_type" : "Equity",
|
"share_type": "Equity",
|
||||||
"from_no" : 201,
|
"from_no": 201,
|
||||||
"to_no" : 500,
|
"to_no": 500,
|
||||||
"no_of_shares" : 300,
|
"no_of_shares": 300,
|
||||||
"rate" : 20,
|
"rate": 20,
|
||||||
"company" : "_Test Company",
|
"company": "_Test Company",
|
||||||
"equity_or_liability_account": "Creditors - _TC"
|
"equity_or_liability_account": "Creditors - _TC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype" : "Share Transfer",
|
"doctype": "Share Transfer",
|
||||||
"transfer_type" : "Transfer",
|
"transfer_type": "Transfer",
|
||||||
"date" : "2018-01-04",
|
"date": "2018-01-04",
|
||||||
"from_shareholder" : "SH-00003",
|
"from_shareholder": "SH-00003",
|
||||||
"to_shareholder" : "SH-00002",
|
"to_shareholder": "SH-00002",
|
||||||
"share_type" : "Equity",
|
"share_type": "Equity",
|
||||||
"from_no" : 201,
|
"from_no": 201,
|
||||||
"to_no" : 400,
|
"to_no": 400,
|
||||||
"no_of_shares" : 200,
|
"no_of_shares": 200,
|
||||||
"rate" : 15,
|
"rate": 15,
|
||||||
"company" : "_Test Company",
|
"company": "_Test Company",
|
||||||
"equity_or_liability_account": "Creditors - _TC"
|
"equity_or_liability_account": "Creditors - _TC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype" : "Share Transfer",
|
"doctype": "Share Transfer",
|
||||||
"transfer_type" : "Purchase",
|
"transfer_type": "Purchase",
|
||||||
"date" : "2018-01-05",
|
"date": "2018-01-05",
|
||||||
"from_shareholder" : "SH-00003",
|
"from_shareholder": "SH-00003",
|
||||||
"share_type" : "Equity",
|
"share_type": "Equity",
|
||||||
"from_no" : 401,
|
"from_no": 401,
|
||||||
"to_no" : 500,
|
"to_no": 500,
|
||||||
"no_of_shares" : 100,
|
"no_of_shares": 100,
|
||||||
"rate" : 25,
|
"rate": 25,
|
||||||
"company" : "_Test Company",
|
"company": "_Test Company",
|
||||||
"asset_account" : "Cash - _TC",
|
"asset_account": "Cash - _TC",
|
||||||
"equity_or_liability_account": "Creditors - _TC"
|
"equity_or_liability_account": "Creditors - _TC"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -91,33 +91,33 @@ class TestShareTransfer(unittest.TestCase):
|
|||||||
|
|
||||||
def test_invalid_share_transfer(self):
|
def test_invalid_share_transfer(self):
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc({
|
||||||
"doctype" : "Share Transfer",
|
"doctype": "Share Transfer",
|
||||||
"transfer_type" : "Transfer",
|
"transfer_type": "Transfer",
|
||||||
"date" : "2018-01-05",
|
"date": "2018-01-05",
|
||||||
"from_shareholder" : "SH-00003",
|
"from_shareholder": "SH-00003",
|
||||||
"to_shareholder" : "SH-00002",
|
"to_shareholder": "SH-00002",
|
||||||
"share_type" : "Equity",
|
"share_type": "Equity",
|
||||||
"from_no" : 1,
|
"from_no": 1,
|
||||||
"to_no" : 100,
|
"to_no": 100,
|
||||||
"no_of_shares" : 100,
|
"no_of_shares": 100,
|
||||||
"rate" : 15,
|
"rate": 15,
|
||||||
"company" : "_Test Company",
|
"company": "_Test Company",
|
||||||
"equity_or_liability_account": "Creditors - _TC"
|
"equity_or_liability_account": "Creditors - _TC"
|
||||||
})
|
})
|
||||||
self.assertRaises(ShareDontExists, doc.insert)
|
self.assertRaises(ShareDontExists, doc.insert)
|
||||||
|
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc({
|
||||||
"doctype" : "Share Transfer",
|
"doctype": "Share Transfer",
|
||||||
"transfer_type" : "Purchase",
|
"transfer_type": "Purchase",
|
||||||
"date" : "2018-01-02",
|
"date": "2018-01-02",
|
||||||
"from_shareholder" : "SH-00001",
|
"from_shareholder": "SH-00001",
|
||||||
"share_type" : "Equity",
|
"share_type": "Equity",
|
||||||
"from_no" : 1,
|
"from_no": 1,
|
||||||
"to_no" : 200,
|
"to_no": 200,
|
||||||
"no_of_shares" : 200,
|
"no_of_shares": 200,
|
||||||
"rate" : 15,
|
"rate": 15,
|
||||||
"company" : "_Test Company",
|
"company": "_Test Company",
|
||||||
"asset_account" : "Cash - _TC",
|
"asset_account": "Cash - _TC",
|
||||||
"equity_or_liability_account": "Creditors - _TC"
|
"equity_or_liability_account": "Creditors - _TC"
|
||||||
})
|
})
|
||||||
self.assertRaises(ShareDontExists, doc.insert)
|
self.assertRaises(ShareDontExists, doc.insert)
|
||||||
|
@ -1,587 +1,163 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"autoname": "naming_series:",
|
||||||
"allow_guest_to_view": 0,
|
"creation": "2017-12-25 16:50:53.878430",
|
||||||
"allow_import": 0,
|
"doctype": "DocType",
|
||||||
"allow_rename": 0,
|
"editable_grid": 1,
|
||||||
"autoname": "naming_series:",
|
"engine": "InnoDB",
|
||||||
"beta": 0,
|
"field_order": [
|
||||||
"creation": "2017-12-25 16:50:53.878430",
|
"title",
|
||||||
"custom": 0,
|
"column_break_2",
|
||||||
"description": "",
|
"naming_series",
|
||||||
"docstatus": 0,
|
"section_break_2",
|
||||||
"doctype": "DocType",
|
"folio_no",
|
||||||
"document_type": "",
|
"column_break_4",
|
||||||
"editable_grid": 1,
|
"company",
|
||||||
"engine": "InnoDB",
|
"is_company",
|
||||||
|
"address_contacts",
|
||||||
|
"address_html",
|
||||||
|
"column_break_9",
|
||||||
|
"contact_html",
|
||||||
|
"section_break_3",
|
||||||
|
"share_balance",
|
||||||
|
"contact_list"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "title",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Data",
|
||||||
"allow_on_submit": 0,
|
"label": "Title",
|
||||||
"bold": 0,
|
"reqd": 1
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "title",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_2",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_2",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "naming_series",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Select",
|
||||||
"allow_on_submit": 0,
|
"options": "ACC-SH-.YYYY.-"
|
||||||
"bold": 0,
|
},
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "",
|
|
||||||
"fieldname": "naming_series",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "ACC-SH-.YYYY.-",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_2",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_2",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "folio_no",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Data",
|
||||||
"allow_on_submit": 0,
|
"label": "Folio no.",
|
||||||
"bold": 0,
|
"read_only": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "folio_no",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Folio no.",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_4",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_4",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "company",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Company",
|
||||||
"collapsible": 0,
|
"options": "Company",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fieldname": "company",
|
},
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "is_company",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Check",
|
||||||
"bold": 0,
|
"hidden": 1,
|
||||||
"collapsible": 0,
|
"label": "Is Company",
|
||||||
"columns": 0,
|
"read_only": 1
|
||||||
"fieldname": "is_company",
|
},
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 1,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Is Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "address_contacts",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break",
|
||||||
"allow_on_submit": 0,
|
"label": "Address and Contacts",
|
||||||
"bold": 0,
|
"options": "fa fa-map-marker"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "address_contacts",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Address and Contacts",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "fa fa-map-marker",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "address_html",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "HTML",
|
||||||
"allow_on_submit": 0,
|
"label": "Address HTML",
|
||||||
"bold": 0,
|
"read_only": 1
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "address_html",
|
|
||||||
"fieldtype": "HTML",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Address HTML",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_9",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_9",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "contact_html",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "HTML",
|
||||||
"allow_on_submit": 0,
|
"label": "Contact HTML",
|
||||||
"bold": 0,
|
"read_only": 1
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "contact_html",
|
|
||||||
"fieldtype": "HTML",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Contact HTML",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_3",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break",
|
||||||
"allow_on_submit": 0,
|
"label": "Share Balance"
|
||||||
"bold": 0,
|
},
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_3",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Share Balance",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "share_balance",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Table",
|
||||||
"allow_on_submit": 0,
|
"label": "Share Balance",
|
||||||
"bold": 0,
|
"options": "Share Balance",
|
||||||
"collapsible": 0,
|
"read_only": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "share_balance",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Share Balance",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Share Balance",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"description": "Hidden list maintaining the list of contacts linked to Shareholder",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "contact_list",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Code",
|
||||||
"bold": 0,
|
"hidden": 1,
|
||||||
"collapsible": 0,
|
"label": "Contact List",
|
||||||
"columns": 0,
|
"read_only": 1
|
||||||
"description": "Hidden list maintaining the list of contacts linked to Shareholder",
|
|
||||||
"fieldname": "contact_list",
|
|
||||||
"fieldtype": "Code",
|
|
||||||
"hidden": 1,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Contact List",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"modified": "2019-11-17 23:24:11.395882",
|
||||||
"hide_heading": 0,
|
"modified_by": "Administrator",
|
||||||
"hide_toolbar": 0,
|
"module": "Accounts",
|
||||||
"idx": 0,
|
"name": "Shareholder",
|
||||||
"image_view": 0,
|
"name_case": "Title Case",
|
||||||
"in_create": 0,
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-09-18 14:14:24.953014",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Shareholder",
|
|
||||||
"name_case": "Title Case",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"delete": 1,
|
||||||
"create": 1,
|
"email": 1,
|
||||||
"delete": 1,
|
"export": 1,
|
||||||
"email": 1,
|
"print": 1,
|
||||||
"export": 1,
|
"read": 1,
|
||||||
"if_owner": 0,
|
"report": 1,
|
||||||
"import": 0,
|
"role": "System Manager",
|
||||||
"permlevel": 0,
|
"share": 1,
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"delete": 1,
|
||||||
"create": 1,
|
"email": 1,
|
||||||
"delete": 1,
|
"export": 1,
|
||||||
"email": 1,
|
"print": 1,
|
||||||
"export": 1,
|
"read": 1,
|
||||||
"if_owner": 0,
|
"report": 1,
|
||||||
"import": 0,
|
"role": "Accounts Manager",
|
||||||
"permlevel": 0,
|
"share": 1,
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"delete": 1,
|
||||||
"create": 1,
|
"email": 1,
|
||||||
"delete": 1,
|
"export": 1,
|
||||||
"email": 1,
|
"print": 1,
|
||||||
"export": 1,
|
"read": 1,
|
||||||
"if_owner": 0,
|
"report": 1,
|
||||||
"import": 0,
|
"role": "Accounts User",
|
||||||
"permlevel": 0,
|
"share": 1,
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"search_fields": "folio_no",
|
||||||
"read_only": 0,
|
"sort_field": "modified",
|
||||||
"read_only_onload": 0,
|
"sort_order": "DESC",
|
||||||
"search_fields": "folio_no",
|
"title_field": "title",
|
||||||
"show_name_in_global_search": 0,
|
"track_changes": 1
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"title_field": "title",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -70,7 +70,7 @@ class ShippingRule(Document):
|
|||||||
|
|
||||||
def get_shipping_amount_from_rules(self, value):
|
def get_shipping_amount_from_rules(self, value):
|
||||||
for condition in self.get("conditions"):
|
for condition in self.get("conditions"):
|
||||||
if not condition.to_value or (flt(condition.from_value) <= value <= flt(condition.to_value)):
|
if not condition.to_value or (flt(condition.from_value) <= flt(value) <= flt(condition.to_value)):
|
||||||
return condition.shipping_amount
|
return condition.shipping_amount
|
||||||
|
|
||||||
return 0.0
|
return 0.0
|
||||||
|
@ -14,13 +14,13 @@ class TestShippingRule(unittest.TestCase):
|
|||||||
shipping_rule.name = test_records[0].get('name')
|
shipping_rule.name = test_records[0].get('name')
|
||||||
shipping_rule.get("conditions")[0].from_value = 101
|
shipping_rule.get("conditions")[0].from_value = 101
|
||||||
self.assertRaises(FromGreaterThanToError, shipping_rule.insert)
|
self.assertRaises(FromGreaterThanToError, shipping_rule.insert)
|
||||||
|
|
||||||
def test_many_zero_to_values(self):
|
def test_many_zero_to_values(self):
|
||||||
shipping_rule = frappe.copy_doc(test_records[0])
|
shipping_rule = frappe.copy_doc(test_records[0])
|
||||||
shipping_rule.name = test_records[0].get('name')
|
shipping_rule.name = test_records[0].get('name')
|
||||||
shipping_rule.get("conditions")[0].to_value = 0
|
shipping_rule.get("conditions")[0].to_value = 0
|
||||||
self.assertRaises(ManyBlankToValuesError, shipping_rule.insert)
|
self.assertRaises(ManyBlankToValuesError, shipping_rule.insert)
|
||||||
|
|
||||||
def test_overlapping_conditions(self):
|
def test_overlapping_conditions(self):
|
||||||
for range_a, range_b in [
|
for range_a, range_b in [
|
||||||
((50, 150), (0, 100)),
|
((50, 150), (0, 100)),
|
||||||
@ -38,6 +38,10 @@ class TestShippingRule(unittest.TestCase):
|
|||||||
self.assertRaises(OverlappingConditionError, shipping_rule.insert)
|
self.assertRaises(OverlappingConditionError, shipping_rule.insert)
|
||||||
|
|
||||||
def create_shipping_rule(shipping_rule_type, shipping_rule_name):
|
def create_shipping_rule(shipping_rule_type, shipping_rule_name):
|
||||||
|
|
||||||
|
if frappe.db.exists("Shipping Rule", shipping_rule_name):
|
||||||
|
return frappe.get_doc("Shipping Rule", shipping_rule_name)
|
||||||
|
|
||||||
sr = frappe.new_doc("Shipping Rule")
|
sr = frappe.new_doc("Shipping Rule")
|
||||||
sr.account = "_Test Account Shipping Charges - _TC"
|
sr.account = "_Test Account Shipping Charges - _TC"
|
||||||
sr.calculate_based_on = "Net Total"
|
sr.calculate_based_on = "Net Total"
|
||||||
@ -70,4 +74,4 @@ def create_shipping_rule(shipping_rule_type, shipping_rule_name):
|
|||||||
})
|
})
|
||||||
sr.insert(ignore_permissions=True)
|
sr.insert(ignore_permissions=True)
|
||||||
sr.submit()
|
sr.submit()
|
||||||
return sr
|
return sr
|
||||||
|
@ -338,6 +338,16 @@ class Subscription(Document):
|
|||||||
|
|
||||||
# Check invoice dates and make sure it doesn't have outstanding invoices
|
# Check invoice dates and make sure it doesn't have outstanding invoices
|
||||||
return getdate(nowdate()) >= getdate(self.current_invoice_start) and not self.has_outstanding_invoice()
|
return getdate(nowdate()) >= getdate(self.current_invoice_start) and not self.has_outstanding_invoice()
|
||||||
|
|
||||||
|
def is_current_invoice_paid(self):
|
||||||
|
if self.is_new_subscription():
|
||||||
|
return False
|
||||||
|
|
||||||
|
last_invoice = frappe.get_doc('Sales Invoice', self.invoices[-1].invoice)
|
||||||
|
if getdate(last_invoice.posting_date) == getdate(self.current_invoice_start) and last_invoice.status == 'Paid':
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def process_for_active(self):
|
def process_for_active(self):
|
||||||
"""
|
"""
|
||||||
@ -348,7 +358,7 @@ class Subscription(Document):
|
|||||||
2. Change the `Subscription` status to 'Past Due Date'
|
2. Change the `Subscription` status to 'Past Due Date'
|
||||||
3. Change the `Subscription` status to 'Cancelled'
|
3. Change the `Subscription` status to 'Cancelled'
|
||||||
"""
|
"""
|
||||||
if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice():
|
if not self.is_current_invoice_paid() and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()):
|
||||||
self.generate_invoice()
|
self.generate_invoice()
|
||||||
if self.current_invoice_is_past_due():
|
if self.current_invoice_is_past_due():
|
||||||
self.status = 'Past Due Date'
|
self.status = 'Past Due Date'
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe.utils import flt, cstr, cint
|
from frappe.utils import flt, cstr, cint, comma_and
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from erpnext.accounts.utils import get_stock_and_account_balance
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
@ -12,6 +13,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import g
|
|||||||
|
|
||||||
class ClosedAccountingPeriod(frappe.ValidationError): pass
|
class ClosedAccountingPeriod(frappe.ValidationError): pass
|
||||||
class StockAccountInvalidTransaction(frappe.ValidationError): pass
|
class StockAccountInvalidTransaction(frappe.ValidationError): pass
|
||||||
|
class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
|
||||||
|
|
||||||
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
|
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
|
||||||
if gl_map:
|
if gl_map:
|
||||||
@ -88,8 +90,12 @@ def merge_similar_entries(gl_map):
|
|||||||
else:
|
else:
|
||||||
merged_gl_map.append(entry)
|
merged_gl_map.append(entry)
|
||||||
|
|
||||||
|
company = gl_map[0].company if gl_map else erpnext.get_default_company()
|
||||||
|
company_currency = erpnext.get_company_currency(company)
|
||||||
|
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
|
||||||
|
|
||||||
# filter zero debit and credit entries
|
# filter zero debit and credit entries
|
||||||
merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map)
|
merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
|
||||||
merged_gl_map = list(merged_gl_map)
|
merged_gl_map = list(merged_gl_map)
|
||||||
|
|
||||||
return merged_gl_map
|
return merged_gl_map
|
||||||
@ -115,11 +121,9 @@ def check_if_in_list(gle, gl_map, dimensions=None):
|
|||||||
|
|
||||||
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
|
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
|
||||||
if not from_repost:
|
if not from_repost:
|
||||||
validate_account_for_perpetual_inventory(gl_map)
|
|
||||||
validate_cwip_accounts(gl_map)
|
validate_cwip_accounts(gl_map)
|
||||||
|
|
||||||
round_off_debit_credit(gl_map)
|
round_off_debit_credit(gl_map)
|
||||||
|
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
make_entry(entry, adv_adj, update_outstanding, from_repost)
|
make_entry(entry, adv_adj, update_outstanding, from_repost)
|
||||||
|
|
||||||
@ -127,6 +131,10 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
|
|||||||
if not from_repost:
|
if not from_repost:
|
||||||
validate_expense_against_budget(entry)
|
validate_expense_against_budget(entry)
|
||||||
|
|
||||||
|
if not from_repost:
|
||||||
|
validate_account_for_perpetual_inventory(gl_map)
|
||||||
|
|
||||||
|
|
||||||
def make_entry(args, adv_adj, update_outstanding, from_repost=False):
|
def make_entry(args, adv_adj, update_outstanding, from_repost=False):
|
||||||
args.update({"doctype": "GL Entry"})
|
args.update({"doctype": "GL Entry"})
|
||||||
gle = frappe.get_doc(args)
|
gle = frappe.get_doc(args)
|
||||||
@ -137,25 +145,67 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False):
|
|||||||
gle.submit()
|
gle.submit()
|
||||||
|
|
||||||
def validate_account_for_perpetual_inventory(gl_map):
|
def validate_account_for_perpetual_inventory(gl_map):
|
||||||
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)) \
|
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)):
|
||||||
and gl_map[0].voucher_type=="Journal Entry":
|
account_list = [gl_entries.account for gl_entries in gl_map]
|
||||||
aii_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
|
|
||||||
where account_type = 'Stock' and is_group=0""")]
|
|
||||||
|
|
||||||
for entry in gl_map:
|
aii_accounts = [d.name for d in frappe.get_all("Account",
|
||||||
if entry.account in aii_accounts:
|
filters={'account_type': 'Stock', 'is_group': 0, 'company': gl_map[0].company})]
|
||||||
|
|
||||||
|
for account in account_list:
|
||||||
|
if account not in aii_accounts:
|
||||||
|
continue
|
||||||
|
|
||||||
|
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account,
|
||||||
|
gl_map[0].posting_date, gl_map[0].company)
|
||||||
|
|
||||||
|
if gl_map[0].voucher_type=="Journal Entry":
|
||||||
|
# In case of Journal Entry, there are no corresponding SL entries,
|
||||||
|
# hence deducting currency amount
|
||||||
|
account_bal -= flt(gl_map[0].debit) - flt(gl_map[0].credit)
|
||||||
|
if account_bal == stock_bal:
|
||||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
||||||
.format(entry.account), StockAccountInvalidTransaction)
|
.format(account), StockAccountInvalidTransaction)
|
||||||
|
|
||||||
|
# This has been comment for a temporary, will add this code again on release of immutable ledger
|
||||||
|
# elif account_bal != stock_bal:
|
||||||
|
# precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
|
||||||
|
# currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
|
||||||
|
|
||||||
|
# diff = flt(stock_bal - account_bal, precision)
|
||||||
|
# error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format(
|
||||||
|
# stock_bal, account_bal, frappe.bold(account))
|
||||||
|
# error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff))
|
||||||
|
# stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account")
|
||||||
|
|
||||||
|
# db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency')
|
||||||
|
# db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency')
|
||||||
|
|
||||||
|
# journal_entry_args = {
|
||||||
|
# 'accounts':[
|
||||||
|
# {'account': account, db_or_cr_warehouse_account : abs(diff)},
|
||||||
|
# {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }]
|
||||||
|
# }
|
||||||
|
|
||||||
|
# frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
|
||||||
|
# raise_exception=StockValueAndAccountBalanceOutOfSync,
|
||||||
|
# title=_('Values Out Of Sync'),
|
||||||
|
# primary_action={
|
||||||
|
# 'label': _('Make Journal Entry'),
|
||||||
|
# 'client_action': 'erpnext.route_to_adjustment_jv',
|
||||||
|
# 'args': journal_entry_args
|
||||||
|
# })
|
||||||
|
|
||||||
def validate_cwip_accounts(gl_map):
|
def validate_cwip_accounts(gl_map):
|
||||||
if not cint(frappe.db.get_value("Asset Settings", None, "disable_cwip_accounting")) \
|
cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
|
||||||
and gl_map[0].voucher_type == "Journal Entry":
|
|
||||||
|
if cwip_enabled and gl_map[0].voucher_type == "Journal Entry":
|
||||||
cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
|
cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
|
||||||
where account_type = 'Capital Work in Progress' and is_group=0""")]
|
where account_type = 'Capital Work in Progress' and is_group=0""")]
|
||||||
|
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
if entry.account in cwip_accounts:
|
if entry.account in cwip_accounts:
|
||||||
frappe.throw(_("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
|
frappe.throw(
|
||||||
|
_("Account: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
|
||||||
|
|
||||||
def round_off_debit_credit(gl_map):
|
def round_off_debit_credit(gl_map):
|
||||||
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
|
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
|
||||||
|
@ -139,15 +139,11 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make() {
|
make() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.upload.make({
|
new frappe.ui.FileUploader({
|
||||||
args: {
|
method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
|
||||||
method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
|
allow_multiple: 0,
|
||||||
allow_multiple: 0
|
on_success: function(attachment, r) {
|
||||||
},
|
|
||||||
no_socketio: true,
|
|
||||||
sample_url: "e.g. http://example.com/somefile.csv",
|
|
||||||
callback: function(attachment, r) {
|
|
||||||
if (!r.exc && r.message) {
|
if (!r.exc && r.message) {
|
||||||
me.data = r.message;
|
me.data = r.message;
|
||||||
me.setup_transactions_dom();
|
me.setup_transactions_dom();
|
||||||
@ -533,9 +529,16 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
|
|||||||
frappe.db.get_doc(dt, event.value)
|
frappe.db.get_doc(dt, event.value)
|
||||||
.then(doc => {
|
.then(doc => {
|
||||||
let displayed_docs = []
|
let displayed_docs = []
|
||||||
|
let payment = []
|
||||||
if (dt === "Payment Entry") {
|
if (dt === "Payment Entry") {
|
||||||
payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency;
|
payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency;
|
||||||
payment.doctype = dt
|
payment.doctype = dt
|
||||||
|
payment.posting_date = doc.posting_date;
|
||||||
|
payment.party = doc.party;
|
||||||
|
payment.reference_no = doc.reference_no;
|
||||||
|
payment.reference_date = doc.reference_date;
|
||||||
|
payment.paid_amount = doc.paid_amount;
|
||||||
|
payment.name = doc.name;
|
||||||
displayed_docs.push(payment);
|
displayed_docs.push(payment);
|
||||||
} else if (dt === "Journal Entry") {
|
} else if (dt === "Journal Entry") {
|
||||||
doc.accounts.forEach(payment => {
|
doc.accounts.forEach(payment => {
|
||||||
@ -568,11 +571,11 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
|
|||||||
|
|
||||||
const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper;
|
const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper;
|
||||||
details_wrapper.append(frappe.render_template("linked_payment_header"));
|
details_wrapper.append(frappe.render_template("linked_payment_header"));
|
||||||
displayed_docs.forEach(values => {
|
displayed_docs.forEach(payment => {
|
||||||
details_wrapper.append(frappe.render_template("linked_payment_row", values));
|
details_wrapper.append(frappe.render_template("linked_payment_row", payment));
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,10 @@ def reconcile(bank_transaction, payment_doctype, payment_name):
|
|||||||
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||||
gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name))
|
gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name))
|
||||||
|
|
||||||
|
if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount:
|
||||||
|
frappe.throw(_("The unallocated amount of Payment Entry {0} \
|
||||||
|
is greater than the Bank Transaction's unallocated amount").format(payment_name))
|
||||||
|
|
||||||
if transaction.unallocated_amount == 0:
|
if transaction.unallocated_amount == 0:
|
||||||
frappe.throw(_("This bank transaction is already fully reconciled"))
|
frappe.throw(_("This bank transaction is already fully reconciled"))
|
||||||
|
|
||||||
@ -373,4 +377,4 @@ def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
'start': start,
|
'start': start,
|
||||||
'page_len': page_len
|
'page_len': page_len
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -23,7 +23,7 @@ class DuplicatePartyAccountError(frappe.ValidationError): pass
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
||||||
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True,
|
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True,
|
||||||
party_address=None, shipping_address=None, pos_profile=None):
|
party_address=None, company_address=None, shipping_address=None, pos_profile=None):
|
||||||
|
|
||||||
if not party:
|
if not party:
|
||||||
return {}
|
return {}
|
||||||
@ -31,14 +31,14 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
|
|||||||
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
||||||
return _get_party_details(party, account, party_type,
|
return _get_party_details(party, account, party_type,
|
||||||
company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions,
|
company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions,
|
||||||
fetch_payment_terms_template, party_address, shipping_address, pos_profile)
|
fetch_payment_terms_template, party_address, company_address, shipping_address, pos_profile)
|
||||||
|
|
||||||
def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
||||||
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False,
|
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False,
|
||||||
fetch_payment_terms_template=True, party_address=None, shipping_address=None, pos_profile=None):
|
fetch_payment_terms_template=True, party_address=None, company_address=None,shipping_address=None, pos_profile=None):
|
||||||
|
|
||||||
out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
party_details = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
||||||
party = out[party_type.lower()]
|
party = party_details[party_type.lower()]
|
||||||
|
|
||||||
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
||||||
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
|
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
|
||||||
@ -46,76 +46,81 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
|||||||
party = frappe.get_doc(party_type, party)
|
party = frappe.get_doc(party_type, party)
|
||||||
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
|
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
|
||||||
|
|
||||||
party_address, shipping_address = set_address_details(out, party, party_type, doctype, company, party_address, shipping_address)
|
party_address, shipping_address = set_address_details(party_details, party, party_type, doctype, company, party_address, company_address, shipping_address)
|
||||||
set_contact_details(out, party, party_type)
|
set_contact_details(party_details, party, party_type)
|
||||||
set_other_values(out, party, party_type)
|
set_other_values(party_details, party, party_type)
|
||||||
set_price_list(out, party, party_type, price_list, pos_profile)
|
set_price_list(party_details, party, party_type, price_list, pos_profile)
|
||||||
|
|
||||||
out["tax_category"] = get_address_tax_category(party.get("tax_category"),
|
party_details["tax_category"] = get_address_tax_category(party.get("tax_category"),
|
||||||
party_address, shipping_address if party_type != "Supplier" else party_address)
|
party_address, shipping_address if party_type != "Supplier" else party_address)
|
||||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company,
|
|
||||||
customer_group=out.customer_group, supplier_group=out.supplier_group, tax_category=out.tax_category,
|
if not party_details.get("taxes_and_charges"):
|
||||||
billing_address=party_address, shipping_address=shipping_address)
|
party_details["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company,
|
||||||
|
customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category,
|
||||||
|
billing_address=party_address, shipping_address=shipping_address)
|
||||||
|
|
||||||
if fetch_payment_terms_template:
|
if fetch_payment_terms_template:
|
||||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
party_details["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||||
|
|
||||||
if not out.get("currency"):
|
if not party_details.get("currency"):
|
||||||
out["currency"] = currency
|
party_details["currency"] = currency
|
||||||
|
|
||||||
# sales team
|
# sales team
|
||||||
if party_type=="Customer":
|
if party_type=="Customer":
|
||||||
out["sales_team"] = [{
|
party_details["sales_team"] = [{
|
||||||
"sales_person": d.sales_person,
|
"sales_person": d.sales_person,
|
||||||
"allocated_percentage": d.allocated_percentage or None
|
"allocated_percentage": d.allocated_percentage or None
|
||||||
} for d in party.get("sales_team")]
|
} for d in party.get("sales_team")]
|
||||||
|
|
||||||
# supplier tax withholding category
|
# supplier tax withholding category
|
||||||
if party_type == "Supplier" and party:
|
if party_type == "Supplier" and party:
|
||||||
out["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
|
party_details["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
|
||||||
|
|
||||||
return out
|
return party_details
|
||||||
|
|
||||||
def set_address_details(out, party, party_type, doctype=None, company=None, party_address=None, shipping_address=None):
|
def set_address_details(party_details, party, party_type, doctype=None, company=None, party_address=None, company_address=None, shipping_address=None):
|
||||||
billing_address_field = "customer_address" if party_type == "Lead" \
|
billing_address_field = "customer_address" if party_type == "Lead" \
|
||||||
else party_type.lower() + "_address"
|
else party_type.lower() + "_address"
|
||||||
out[billing_address_field] = party_address or get_default_address(party_type, party.name)
|
party_details[billing_address_field] = party_address or get_default_address(party_type, party.name)
|
||||||
if doctype:
|
if doctype:
|
||||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
party_details.update(get_fetch_values(doctype, billing_address_field, party_details[billing_address_field]))
|
||||||
# address display
|
# address display
|
||||||
out.address_display = get_address_display(out[billing_address_field])
|
party_details.address_display = get_address_display(party_details[billing_address_field])
|
||||||
# shipping address
|
# shipping address
|
||||||
if party_type in ["Customer", "Lead"]:
|
if party_type in ["Customer", "Lead"]:
|
||||||
out.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name)
|
party_details.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name)
|
||||||
out.shipping_address = get_address_display(out["shipping_address_name"])
|
party_details.shipping_address = get_address_display(party_details["shipping_address_name"])
|
||||||
if doctype:
|
if doctype:
|
||||||
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
party_details.update(get_fetch_values(doctype, 'shipping_address_name', party_details.shipping_address_name))
|
||||||
|
|
||||||
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
|
if company_address:
|
||||||
out.update(get_company_address(company))
|
party_details.update({'company_address': company_address})
|
||||||
if out.company_address:
|
else:
|
||||||
out.update(get_fetch_values(doctype, 'company_address', out.company_address))
|
party_details.update(get_company_address(company))
|
||||||
get_regional_address_details(out, doctype, company)
|
|
||||||
|
|
||||||
elif doctype and doctype == "Purchase Invoice":
|
if doctype and doctype in ['Delivery Note', 'Sales Invoice', 'Sales Order']:
|
||||||
out.update(get_company_address(company))
|
if party_details.company_address:
|
||||||
if out.company_address:
|
party_details.update(get_fetch_values(doctype, 'company_address', party_details.company_address))
|
||||||
out["shipping_address"] = shipping_address or out["company_address"]
|
get_regional_address_details(party_details, doctype, company)
|
||||||
out.shipping_address_display = get_address_display(out["shipping_address"])
|
|
||||||
out.update(get_fetch_values(doctype, 'shipping_address', out.shipping_address))
|
|
||||||
get_regional_address_details(out, doctype, company)
|
|
||||||
|
|
||||||
return out.get(billing_address_field), out.shipping_address_name
|
elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
|
||||||
|
if party_details.company_address:
|
||||||
|
party_details["shipping_address"] = shipping_address or party_details["company_address"]
|
||||||
|
party_details.shipping_address_display = get_address_display(party_details["shipping_address"])
|
||||||
|
party_details.update(get_fetch_values(doctype, 'shipping_address', party_details.shipping_address))
|
||||||
|
get_regional_address_details(party_details, doctype, company)
|
||||||
|
|
||||||
|
return party_details.get(billing_address_field), party_details.shipping_address_name
|
||||||
|
|
||||||
@erpnext.allow_regional
|
@erpnext.allow_regional
|
||||||
def get_regional_address_details(out, doctype, company):
|
def get_regional_address_details(party_details, doctype, company):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_contact_details(out, party, party_type):
|
def set_contact_details(party_details, party, party_type):
|
||||||
out.contact_person = get_default_contact(party_type, party.name)
|
party_details.contact_person = get_default_contact(party_type, party.name)
|
||||||
|
|
||||||
if not out.contact_person:
|
if not party_details.contact_person:
|
||||||
out.update({
|
party_details.update({
|
||||||
"contact_person": None,
|
"contact_person": None,
|
||||||
"contact_display": None,
|
"contact_display": None,
|
||||||
"contact_email": None,
|
"contact_email": None,
|
||||||
@ -125,22 +130,22 @@ def set_contact_details(out, party, party_type):
|
|||||||
"contact_department": None
|
"contact_department": None
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
out.update(get_contact_details(out.contact_person))
|
party_details.update(get_contact_details(party_details.contact_person))
|
||||||
|
|
||||||
def set_other_values(out, party, party_type):
|
def set_other_values(party_details, party, party_type):
|
||||||
# copy
|
# copy
|
||||||
if party_type=="Customer":
|
if party_type=="Customer":
|
||||||
to_copy = ["customer_name", "customer_group", "territory", "language"]
|
to_copy = ["customer_name", "customer_group", "territory", "language"]
|
||||||
else:
|
else:
|
||||||
to_copy = ["supplier_name", "supplier_group", "language"]
|
to_copy = ["supplier_name", "supplier_group", "language"]
|
||||||
for f in to_copy:
|
for f in to_copy:
|
||||||
out[f] = party.get(f)
|
party_details[f] = party.get(f)
|
||||||
|
|
||||||
# fields prepended with default in Customer doctype
|
# fields prepended with default in Customer doctype
|
||||||
for f in ['currency'] \
|
for f in ['currency'] \
|
||||||
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
|
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
|
||||||
if party.get("default_" + f):
|
if party.get("default_" + f):
|
||||||
out[f] = party.get("default_" + f)
|
party_details[f] = party.get("default_" + f)
|
||||||
|
|
||||||
def get_default_price_list(party):
|
def get_default_price_list(party):
|
||||||
"""Return default price list for party (Document object)"""
|
"""Return default price list for party (Document object)"""
|
||||||
@ -155,7 +160,7 @@ def get_default_price_list(party):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def set_price_list(out, party, party_type, given_price_list, pos=None):
|
def set_price_list(party_details, party, party_type, given_price_list, pos=None):
|
||||||
# price list
|
# price list
|
||||||
price_list = get_permitted_documents('Price List')
|
price_list = get_permitted_documents('Price List')
|
||||||
|
|
||||||
@ -173,9 +178,9 @@ def set_price_list(out, party, party_type, given_price_list, pos=None):
|
|||||||
price_list = get_default_price_list(party) or given_price_list
|
price_list = get_default_price_list(party) or given_price_list
|
||||||
|
|
||||||
if price_list:
|
if price_list:
|
||||||
out.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True)
|
party_details.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True)
|
||||||
|
|
||||||
out["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
|
party_details["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
|
||||||
|
|
||||||
|
|
||||||
def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
|
def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="right" colspan="3" ><strong>Total (debit) </strong></td>
|
<td class="right" colspan="3" ><strong>Total (debit) </strong></td>
|
||||||
<td class="left" >{{ gl | sum(attribute='debit') }}</td>
|
<td class="left" >{{ frappe.format((gl | sum(attribute="debit")), {fieldtype: "Currency"}) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="top-bottom" colspan="5"><strong>Credit</strong></td>
|
<td class="top-bottom" colspan="5"><strong>Credit</strong></td>
|
||||||
@ -69,7 +69,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="right" colspan="3"><strong>Total (credit) </strong></td>
|
<td class="right" colspan="3"><strong>Total (credit) </strong></td>
|
||||||
<td class="left" >{{ gl | sum(attribute='credit') }}</td>
|
<td class="left" >{{ frappe.format((gl | sum(attribute="credit")), {fieldtype: "Currency"}) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,22 +1,23 @@
|
|||||||
{
|
{
|
||||||
"align_labels_right": 0,
|
"align_labels_right": 0,
|
||||||
"creation": "2017-08-08 12:33:04.773099",
|
"creation": "2017-08-08 12:33:04.773099",
|
||||||
"custom_format": 1,
|
"custom_format": 1,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"doc_type": "Sales Invoice",
|
"doc_type": "Sales Invoice",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Print Format",
|
"doctype": "Print Format",
|
||||||
"font": "Default",
|
"font": "Default",
|
||||||
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t<b>{{ _(\"GSTIN\") }}:</b>{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"<br>GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t<br>\n\t{% if doc.docstatus == 0 %}\n\t\t<b>{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}</b><br>\n\t{% else %}\n\t\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n\t{% endif %}\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t<b>{{ _(\"Customer\") }}:</b><br>\n\t\t{{ doc.customer_name }}<br>\n\t\t{{ customer_address }}\n\t{% endif %}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t<br><b>{{ _(\"HSN/SAC\") }}:</b> {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"Serial No\") }}:</b> {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.rate }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if (not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) and row.tax_amount != 0 -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t{%- if doc.change_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t{%- endif -%}\n\t</tbody>\n</table>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
|
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t<b>{{ _(\"GSTIN\") }}:</b>{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"<br>GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t<br>\n\t{% if doc.docstatus == 0 %}\n\t\t<b>{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}</b><br>\n\t{% else %}\n\t\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n\t{% endif %}\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t<b>{{ _(\"Customer\") }}:</b><br>\n\t\t{{ doc.customer_name }}<br>\n\t\t{{ customer_address }}\n\t{% endif %}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t<br><b>{{ _(\"HSN/SAC\") }}:</b> {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"Serial No\") }}:</b> {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.rate }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if (not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) and row.tax_amount != 0 -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t{%- if doc.change_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t{%- endif -%}\n\t</tbody>\n</table>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"line_breaks": 0,
|
"line_breaks": 0,
|
||||||
"modified": "2019-01-24 17:09:27.190929",
|
"modified": "2019-12-09 17:39:23.356573",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "GST POS Invoice",
|
"name": "GST POS Invoice",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"print_format_builder": 0,
|
"print_format_builder": 0,
|
||||||
"print_format_type": "Server",
|
"print_format_type": "Jinja",
|
||||||
"show_section_headings": 0,
|
"raw_printing": 0,
|
||||||
|
"show_section_headings": 0,
|
||||||
"standard": "Yes"
|
"standard": "Yes"
|
||||||
}
|
}
|
@ -1,21 +1,22 @@
|
|||||||
{
|
{
|
||||||
"align_labels_right": 0,
|
"align_labels_right": 0,
|
||||||
"creation": "2011-12-21 11:08:55",
|
"creation": "2011-12-21 11:08:55",
|
||||||
"custom_format": 1,
|
"custom_format": 1,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"doc_type": "Sales Invoice",
|
"doc_type": "Sales Invoice",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Print Format",
|
"doctype": "Print Format",
|
||||||
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \" + (doc.select_print_heading or _(\"Invoice\")) }}<br>\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n\t{% endif %}\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.get_formatted(\"rate\") }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.change_amount -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- if doc.pos_total_qty -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Total Qty\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"pos_total_qty\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
|
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.get_formatted(\"rate\") }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.change_amount -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"line_breaks": 0,
|
"line_breaks": 0,
|
||||||
"modified": "2018-03-20 14:24:12.394354",
|
"modified": "2019-12-09 17:40:53.183574",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Invoice",
|
"name": "POS Invoice",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"print_format_builder": 0,
|
"print_format_builder": 0,
|
||||||
"print_format_type": "Server",
|
"print_format_type": "Jinja",
|
||||||
"show_section_headings": 0,
|
"raw_printing": 0,
|
||||||
|
"show_section_headings": 0,
|
||||||
"standard": "Yes"
|
"standard": "Yes"
|
||||||
}
|
}
|
@ -100,6 +100,11 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"based_on_payment_terms",
|
||||||
|
"label": __("Based On Payment Terms"),
|
||||||
|
"fieldtype": "Check",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"tax_id",
|
"fieldname":"tax_id",
|
||||||
"label": __("Tax Id"),
|
"label": __("Tax Id"),
|
||||||
|
@ -88,6 +88,11 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
|||||||
"label": __("Supplier Group"),
|
"label": __("Supplier Group"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"based_on_payment_terms",
|
||||||
|
"label": __("Based On Payment Terms"),
|
||||||
|
"fieldtype": "Check",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -79,13 +79,20 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
"options": "Customer",
|
"options": "Customer",
|
||||||
on_change: () => {
|
on_change: () => {
|
||||||
var customer = frappe.query_report.get_filter_value('customer');
|
var customer = frappe.query_report.get_filter_value('customer');
|
||||||
|
var company = frappe.query_report.get_filter_value('company');
|
||||||
if (customer) {
|
if (customer) {
|
||||||
frappe.db.get_value('Customer', customer, ["tax_id", "customer_name", "credit_limit", "payment_terms"], function(value) {
|
frappe.db.get_value('Customer', customer, ["tax_id", "customer_name", "payment_terms"], function(value) {
|
||||||
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
|
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
|
||||||
frappe.query_report.set_filter_value('customer_name', value["customer_name"]);
|
frappe.query_report.set_filter_value('customer_name', value["customer_name"]);
|
||||||
frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]);
|
|
||||||
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
|
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.db.get_value('Customer Credit Limit', {'parent': customer, 'company': company},
|
||||||
|
["credit_limit"], function(value) {
|
||||||
|
if (value) {
|
||||||
|
frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]);
|
||||||
|
}
|
||||||
|
}, "Customer");
|
||||||
} else {
|
} else {
|
||||||
frappe.query_report.set_filter_value('tax_id', "");
|
frappe.query_report.set_filter_value('tax_id', "");
|
||||||
frappe.query_report.set_filter_value('customer_name', "");
|
frappe.query_report.set_filter_value('customer_name', "");
|
||||||
|
@ -60,6 +60,7 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
self.get_gl_entries()
|
self.get_gl_entries()
|
||||||
|
self.get_sales_invoices_or_customers_based_on_sales_person()
|
||||||
self.voucher_balance = OrderedDict()
|
self.voucher_balance = OrderedDict()
|
||||||
self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
|
self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
|
||||||
|
|
||||||
@ -103,12 +104,18 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
def get_invoices(self, gle):
|
def get_invoices(self, gle):
|
||||||
if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
|
if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
|
||||||
self.invoices.add(gle.voucher_no)
|
if self.filters.get("sales_person"):
|
||||||
|
if gle.voucher_no in self.sales_person_records.get("Sales Invoice", []) \
|
||||||
|
or gle.party in self.sales_person_records.get("Customer", []):
|
||||||
|
self.invoices.add(gle.voucher_no)
|
||||||
|
else:
|
||||||
|
self.invoices.add(gle.voucher_no)
|
||||||
|
|
||||||
def update_voucher_balance(self, gle):
|
def update_voucher_balance(self, gle):
|
||||||
# get the row where this balance needs to be updated
|
# get the row where this balance needs to be updated
|
||||||
# if its a payment, it will return the linked invoice or will be considered as advance
|
# if its a payment, it will return the linked invoice or will be considered as advance
|
||||||
row = self.get_voucher_balance(gle)
|
row = self.get_voucher_balance(gle)
|
||||||
|
if not row: return
|
||||||
# gle_balance will be the total "debit - credit" for receivable type reports and
|
# gle_balance will be the total "debit - credit" for receivable type reports and
|
||||||
# and vice-versa for payable type reports
|
# and vice-versa for payable type reports
|
||||||
gle_balance = self.get_gle_balance(gle)
|
gle_balance = self.get_gle_balance(gle)
|
||||||
@ -129,8 +136,13 @@ class ReceivablePayableReport(object):
|
|||||||
row.paid -= gle_balance
|
row.paid -= gle_balance
|
||||||
|
|
||||||
def get_voucher_balance(self, gle):
|
def get_voucher_balance(self, gle):
|
||||||
voucher_balance = None
|
if self.filters.get("sales_person"):
|
||||||
|
against_voucher = gle.against_voucher or gle.voucher_no
|
||||||
|
if not (gle.party in self.sales_person_records.get("Customer", []) or \
|
||||||
|
against_voucher in self.sales_person_records.get("Sales Invoice", [])):
|
||||||
|
return
|
||||||
|
|
||||||
|
voucher_balance = None
|
||||||
if gle.against_voucher:
|
if gle.against_voucher:
|
||||||
# find invoice
|
# find invoice
|
||||||
against_voucher = gle.against_voucher
|
against_voucher = gle.against_voucher
|
||||||
@ -159,7 +171,7 @@ class ReceivablePayableReport(object):
|
|||||||
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
|
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
|
||||||
row.invoice_grand_total = row.invoiced
|
row.invoice_grand_total = row.invoiced
|
||||||
|
|
||||||
if abs(row.outstanding) > 0.1/10 ** self.currency_precision:
|
if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
|
||||||
# non-zero oustanding, we must consider this row
|
# non-zero oustanding, we must consider this row
|
||||||
|
|
||||||
if self.is_invoice(row) and self.filters.based_on_payment_terms:
|
if self.is_invoice(row) and self.filters.based_on_payment_terms:
|
||||||
@ -188,7 +200,11 @@ class ReceivablePayableReport(object):
|
|||||||
self.data.append(row)
|
self.data.append(row)
|
||||||
|
|
||||||
def set_invoice_details(self, row):
|
def set_invoice_details(self, row):
|
||||||
row.update(self.invoice_details.get(row.voucher_no, {}))
|
invoice_details = self.invoice_details.get(row.voucher_no, {})
|
||||||
|
if row.due_date:
|
||||||
|
invoice_details.pop("due_date", None)
|
||||||
|
row.update(invoice_details)
|
||||||
|
|
||||||
if row.voucher_type == 'Sales Invoice':
|
if row.voucher_type == 'Sales Invoice':
|
||||||
if self.filters.show_delivery_notes:
|
if self.filters.show_delivery_notes:
|
||||||
self.set_delivery_notes(row)
|
self.set_delivery_notes(row)
|
||||||
@ -269,7 +285,7 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
def set_party_details(self, row):
|
def set_party_details(self, row):
|
||||||
# customer / supplier name
|
# customer / supplier name
|
||||||
party_details = self.get_party_details(row.party)
|
party_details = self.get_party_details(row.party) or {}
|
||||||
row.update(party_details)
|
row.update(party_details)
|
||||||
if self.filters.get(scrub(self.filters.party_type)):
|
if self.filters.get(scrub(self.filters.party_type)):
|
||||||
row.currency = row.account_currency
|
row.currency = row.account_currency
|
||||||
@ -314,7 +330,7 @@ class ReceivablePayableReport(object):
|
|||||||
self.append_payment_term(row, d, term)
|
self.append_payment_term(row, d, term)
|
||||||
|
|
||||||
def append_payment_term(self, row, d, term):
|
def append_payment_term(self, row, d, term):
|
||||||
if self.filters.get("customer") and d.currency == d.party_account_currency:
|
if (self.filters.get("customer") or self.filters.get("supplier")) and d.currency == d.party_account_currency:
|
||||||
invoiced = d.payment_amount
|
invoiced = d.payment_amount
|
||||||
else:
|
else:
|
||||||
invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
|
invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
|
||||||
@ -508,6 +524,22 @@ class ReceivablePayableReport(object):
|
|||||||
order by posting_date, party"""
|
order by posting_date, party"""
|
||||||
.format(select_fields, conditions), values, as_dict=True)
|
.format(select_fields, conditions), values, as_dict=True)
|
||||||
|
|
||||||
|
def get_sales_invoices_or_customers_based_on_sales_person(self):
|
||||||
|
if self.filters.get("sales_person"):
|
||||||
|
lft, rgt = frappe.db.get_value("Sales Person",
|
||||||
|
self.filters.get("sales_person"), ["lft", "rgt"])
|
||||||
|
|
||||||
|
records = frappe.db.sql("""
|
||||||
|
select distinct parent, parenttype
|
||||||
|
from `tabSales Team` steam
|
||||||
|
where parenttype in ('Customer', 'Sales Invoice')
|
||||||
|
and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person)
|
||||||
|
""", (lft, rgt), as_dict=1)
|
||||||
|
|
||||||
|
self.sales_person_records = frappe._dict()
|
||||||
|
for d in records:
|
||||||
|
self.sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
|
||||||
|
|
||||||
def prepare_conditions(self):
|
def prepare_conditions(self):
|
||||||
conditions = [""]
|
conditions = [""]
|
||||||
values = [self.party_type, self.filters.report_date]
|
values = [self.party_type, self.filters.report_date]
|
||||||
@ -560,16 +592,6 @@ class ReceivablePayableReport(object):
|
|||||||
conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
|
conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
|
||||||
values.append(self.filters.get("sales_partner"))
|
values.append(self.filters.get("sales_partner"))
|
||||||
|
|
||||||
if self.filters.get("sales_person"):
|
|
||||||
lft, rgt = frappe.db.get_value("Sales Person",
|
|
||||||
self.filters.get("sales_person"), ["lft", "rgt"])
|
|
||||||
|
|
||||||
conditions.append("""exists(select name from `tabSales Team` steam where
|
|
||||||
steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1})
|
|
||||||
and ((steam.parent = voucher_no and steam.parenttype = voucher_type)
|
|
||||||
or (steam.parent = against_voucher and steam.parenttype = against_voucher_type)
|
|
||||||
or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt))
|
|
||||||
|
|
||||||
def add_supplier_filters(self, conditions, values):
|
def add_supplier_filters(self, conditions, values):
|
||||||
if self.filters.get("supplier_group"):
|
if self.filters.get("supplier_group"):
|
||||||
conditions.append("""party in (select name from tabSupplier
|
conditions.append("""party in (select name from tabSupplier
|
||||||
|
@ -106,6 +106,11 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
|||||||
"label": __("Sales Person"),
|
"label": __("Sales Person"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Sales Person"
|
"options": "Sales Person"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"based_on_payment_terms",
|
||||||
|
"label": __("Based On Payment Terms"),
|
||||||
|
"fieldtype": "Check",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -36,6 +36,9 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
|||||||
self.filters.report_date) or {}
|
self.filters.report_date) or {}
|
||||||
|
|
||||||
for party, party_dict in iteritems(self.party_total):
|
for party, party_dict in iteritems(self.party_total):
|
||||||
|
if party_dict.outstanding == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
row = frappe._dict()
|
row = frappe._dict()
|
||||||
|
|
||||||
row.party = party
|
row.party = party
|
||||||
|
@ -4,126 +4,141 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import formatdate, getdate, flt, add_days
|
from frappe.utils import formatdate, flt, add_days
|
||||||
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
filters.day_before_from_date = add_days(filters.from_date, -1)
|
filters.day_before_from_date = add_days(filters.from_date, -1)
|
||||||
columns, data = get_columns(filters), get_data(filters)
|
columns, data = get_columns(filters), get_data(filters)
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
asset_categories = get_asset_categories(filters)
|
asset_categories = get_asset_categories(filters)
|
||||||
assets = get_assets(filters)
|
assets = get_assets(filters)
|
||||||
asset_costs = get_asset_costs(assets, filters)
|
|
||||||
asset_depreciations = get_accumulated_depreciations(assets, filters)
|
|
||||||
|
|
||||||
for asset_category in asset_categories:
|
for asset_category in asset_categories:
|
||||||
row = frappe._dict()
|
row = frappe._dict()
|
||||||
row.asset_category = asset_category
|
# row.asset_category = asset_category
|
||||||
row.update(asset_costs.get(asset_category))
|
row.update(asset_category)
|
||||||
|
|
||||||
|
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) -
|
||||||
|
flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
|
||||||
|
|
||||||
|
row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", "")))
|
||||||
|
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
|
||||||
|
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
|
||||||
|
|
||||||
|
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
|
||||||
|
flt(row.accumulated_depreciation_as_on_from_date))
|
||||||
|
|
||||||
|
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
|
||||||
|
flt(row.accumulated_depreciation_as_on_to_date))
|
||||||
|
|
||||||
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase)
|
|
||||||
- flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
|
|
||||||
|
|
||||||
row.update(asset_depreciations.get(asset_category))
|
|
||||||
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
|
|
||||||
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
|
|
||||||
|
|
||||||
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
|
|
||||||
flt(row.accumulated_depreciation_as_on_from_date))
|
|
||||||
|
|
||||||
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
|
|
||||||
flt(row.accumulated_depreciation_as_on_to_date))
|
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_asset_categories(filters):
|
def get_asset_categories(filters):
|
||||||
return frappe.db.sql_list("""
|
return frappe.db.sql("""
|
||||||
select distinct asset_category from `tabAsset`
|
SELECT asset_category,
|
||||||
where docstatus=1 and company=%s and purchase_date <= %s
|
ifnull(sum(case when purchase_date < %(from_date)s then
|
||||||
""", (filters.company, filters.to_date))
|
case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
|
||||||
|
gross_purchase_amount
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end), 0) as cost_as_on_from_date,
|
||||||
|
ifnull(sum(case when purchase_date >= %(from_date)s then
|
||||||
|
gross_purchase_amount
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end), 0) as cost_of_new_purchase,
|
||||||
|
ifnull(sum(case when ifnull(disposal_date, 0) != 0
|
||||||
|
and disposal_date >= %(from_date)s
|
||||||
|
and disposal_date <= %(to_date)s then
|
||||||
|
case when status = "Sold" then
|
||||||
|
gross_purchase_amount
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end), 0) as cost_of_sold_asset,
|
||||||
|
ifnull(sum(case when ifnull(disposal_date, 0) != 0
|
||||||
|
and disposal_date >= %(from_date)s
|
||||||
|
and disposal_date <= %(to_date)s then
|
||||||
|
case when status = "Scrapped" then
|
||||||
|
gross_purchase_amount
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end), 0) as cost_of_scrapped_asset
|
||||||
|
from `tabAsset`
|
||||||
|
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s
|
||||||
|
group by asset_category
|
||||||
|
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
|
||||||
|
|
||||||
|
|
||||||
def get_assets(filters):
|
def get_assets(filters):
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select name, asset_category, purchase_date, gross_purchase_amount, disposal_date, status
|
SELECT results.asset_category,
|
||||||
from `tabAsset`
|
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
|
||||||
where docstatus=1 and company=%s and purchase_date <= %s""",
|
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
|
||||||
(filters.company, filters.to_date), as_dict=1)
|
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
|
||||||
|
from (SELECT a.asset_category,
|
||||||
def get_asset_costs(assets, filters):
|
ifnull(sum(a.opening_accumulated_depreciation +
|
||||||
asset_costs = frappe._dict()
|
case when ds.schedule_date < %(from_date)s and
|
||||||
for d in assets:
|
(ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
|
||||||
asset_costs.setdefault(d.asset_category, frappe._dict({
|
ds.depreciation_amount
|
||||||
"cost_as_on_from_date": 0,
|
else
|
||||||
"cost_of_new_purchase": 0,
|
0
|
||||||
"cost_of_sold_asset": 0,
|
end), 0) as accumulated_depreciation_as_on_from_date,
|
||||||
"cost_of_scrapped_asset": 0
|
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
|
||||||
}))
|
and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
|
||||||
|
ds.depreciation_amount
|
||||||
costs = asset_costs[d.asset_category]
|
else
|
||||||
|
0
|
||||||
if getdate(d.purchase_date) < getdate(filters.from_date):
|
end), 0) as depreciation_eliminated_during_the_period,
|
||||||
if not d.disposal_date or getdate(d.disposal_date) >= getdate(filters.from_date):
|
|
||||||
costs.cost_as_on_from_date += flt(d.gross_purchase_amount)
|
|
||||||
else:
|
|
||||||
costs.cost_of_new_purchase += flt(d.gross_purchase_amount)
|
|
||||||
|
|
||||||
if d.disposal_date and getdate(d.disposal_date) >= getdate(filters.from_date) \
|
|
||||||
and getdate(d.disposal_date) <= getdate(filters.to_date):
|
|
||||||
if d.status == "Sold":
|
|
||||||
costs.cost_of_sold_asset += flt(d.gross_purchase_amount)
|
|
||||||
elif d.status == "Scrapped":
|
|
||||||
costs.cost_of_scrapped_asset += flt(d.gross_purchase_amount)
|
|
||||||
|
|
||||||
return asset_costs
|
|
||||||
|
|
||||||
def get_accumulated_depreciations(assets, filters):
|
|
||||||
asset_depreciations = frappe._dict()
|
|
||||||
for d in assets:
|
|
||||||
asset = frappe.get_doc("Asset", d.name)
|
|
||||||
|
|
||||||
if d.asset_category in asset_depreciations:
|
|
||||||
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] += asset.opening_accumulated_depreciation
|
|
||||||
else:
|
|
||||||
asset_depreciations.setdefault(d.asset_category, frappe._dict({
|
|
||||||
"accumulated_depreciation_as_on_from_date": asset.opening_accumulated_depreciation,
|
|
||||||
"depreciation_amount_during_the_period": 0,
|
|
||||||
"depreciation_eliminated_during_the_period": 0
|
|
||||||
}))
|
|
||||||
|
|
||||||
depr = asset_depreciations[d.asset_category]
|
ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
|
||||||
|
and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
|
||||||
|
ds.depreciation_amount
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end), 0) as depreciation_amount_during_the_period
|
||||||
|
from `tabAsset` a, `tabDepreciation Schedule` ds
|
||||||
|
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent
|
||||||
|
group by a.asset_category
|
||||||
|
union
|
||||||
|
SELECT a.asset_category,
|
||||||
|
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||||
|
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
|
||||||
|
0
|
||||||
|
else
|
||||||
|
a.opening_accumulated_depreciation
|
||||||
|
end), 0) as accumulated_depreciation_as_on_from_date,
|
||||||
|
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
|
||||||
|
a.opening_accumulated_depreciation
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end), 0) as depreciation_eliminated_during_the_period,
|
||||||
|
0 as depreciation_amount_during_the_period
|
||||||
|
from `tabAsset` a
|
||||||
|
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s
|
||||||
|
and not exists(select * from `tabDepreciation Schedule` ds where a.name = ds.parent)
|
||||||
|
group by a.asset_category) as results
|
||||||
|
group by results.asset_category
|
||||||
|
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
|
||||||
|
|
||||||
if not asset.schedules: # if no schedule,
|
|
||||||
if asset.disposal_date:
|
|
||||||
# and disposal is NOT within the period, then opening accumulated depreciation not included
|
|
||||||
if getdate(asset.disposal_date) < getdate(filters.from_date) or getdate(asset.disposal_date) > getdate(filters.to_date):
|
|
||||||
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] = 0
|
|
||||||
|
|
||||||
# if no schedule, and disposal is within period, accumulated dep is the amount eliminated
|
|
||||||
if getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
|
|
||||||
depr.depreciation_eliminated_during_the_period += asset.opening_accumulated_depreciation
|
|
||||||
|
|
||||||
for schedule in asset.get("schedules"):
|
|
||||||
if getdate(schedule.schedule_date) < getdate(filters.from_date):
|
|
||||||
if not asset.disposal_date or getdate(asset.disposal_date) >= getdate(filters.from_date):
|
|
||||||
depr.accumulated_depreciation_as_on_from_date += flt(schedule.depreciation_amount)
|
|
||||||
elif getdate(schedule.schedule_date) <= getdate(filters.to_date):
|
|
||||||
if not asset.disposal_date:
|
|
||||||
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
|
|
||||||
else:
|
|
||||||
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
|
|
||||||
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
|
|
||||||
|
|
||||||
if asset.disposal_date and getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
|
|
||||||
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
|
|
||||||
depr.depreciation_eliminated_during_the_period += flt(schedule.depreciation_amount)
|
|
||||||
|
|
||||||
return asset_depreciations
|
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||||
frappe.query_reports["Balance Sheet"] = erpnext.financial_statements;
|
frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
|
||||||
|
|
||||||
frappe.query_reports["Balance Sheet"]["filters"].push({
|
frappe.query_reports["Balance Sheet"]["filters"].push({
|
||||||
"fieldname": "accumulated_values",
|
"fieldname": "accumulated_values",
|
||||||
|
@ -46,13 +46,24 @@ frappe.query_reports["Budget Variance Report"] = {
|
|||||||
fieldtype: "Select",
|
fieldtype: "Select",
|
||||||
options: ["Cost Center", "Project"],
|
options: ["Cost Center", "Project"],
|
||||||
default: "Cost Center",
|
default: "Cost Center",
|
||||||
reqd: 1
|
reqd: 1,
|
||||||
|
on_change: function() {
|
||||||
|
frappe.query_report.set_filter_value("budget_against_filter", []);
|
||||||
|
frappe.query_report.refresh();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: "cost_center",
|
fieldname:"budget_against_filter",
|
||||||
label: __("Cost Center"),
|
label: __('Dimension Filter'),
|
||||||
fieldtype: "Link",
|
fieldtype: "MultiSelectList",
|
||||||
options: "Cost Center"
|
get_data: function(txt) {
|
||||||
|
if (!frappe.query_report.filters) return;
|
||||||
|
|
||||||
|
let budget_against = frappe.query_report.get_filter_value('budget_against');
|
||||||
|
if (!budget_against) return;
|
||||||
|
|
||||||
|
return frappe.db.get_link_options(budget_against, txt);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname:"show_cumulative",
|
fieldname:"show_cumulative",
|
||||||
|
@ -12,22 +12,22 @@ from six import iteritems
|
|||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
if not filters: filters = {}
|
if not filters: filters = {}
|
||||||
validate_filters(filters)
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
if filters.get("cost_center"):
|
if filters.get("budget_against_filter"):
|
||||||
cost_centers = [filters.get("cost_center")]
|
dimensions = filters.get("budget_against_filter")
|
||||||
else:
|
else:
|
||||||
cost_centers = get_cost_centers(filters)
|
dimensions = get_cost_centers(filters)
|
||||||
|
|
||||||
period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"])
|
period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"])
|
||||||
cam_map = get_cost_center_account_month_map(filters)
|
cam_map = get_dimension_account_month_map(filters)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for cost_center in cost_centers:
|
for dimension in dimensions:
|
||||||
cost_center_items = cam_map.get(cost_center)
|
dimension_items = cam_map.get(dimension)
|
||||||
if cost_center_items:
|
if dimension_items:
|
||||||
for account, monthwise_data in iteritems(cost_center_items):
|
for account, monthwise_data in iteritems(dimension_items):
|
||||||
row = [cost_center, account]
|
row = [dimension, account]
|
||||||
totals = [0, 0, 0]
|
totals = [0, 0, 0]
|
||||||
for year in get_fiscal_years(filters):
|
for year in get_fiscal_years(filters):
|
||||||
last_total = 0
|
last_total = 0
|
||||||
@ -55,10 +55,6 @@ def execute(filters=None):
|
|||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def validate_filters(filters):
|
|
||||||
if filters.get("budget_against") != "Cost Center" and filters.get("cost_center"):
|
|
||||||
frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center"))
|
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"]
|
columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"]
|
||||||
|
|
||||||
@ -98,11 +94,12 @@ def get_cost_centers(filters):
|
|||||||
else:
|
else:
|
||||||
return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec
|
return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec
|
||||||
|
|
||||||
#Get cost center & target details
|
#Get dimension & target details
|
||||||
def get_cost_center_target_details(filters):
|
def get_dimension_target_details(filters):
|
||||||
cond = ""
|
cond = ""
|
||||||
if filters.get("cost_center"):
|
if filters.get("budget_against_filter"):
|
||||||
cond += " and b.cost_center=%s" % frappe.db.escape(filters.get("cost_center"))
|
cond += " and b.{budget_against} in (%s)".format(budget_against = \
|
||||||
|
frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter')))
|
||||||
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
|
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
|
||||||
@ -110,8 +107,8 @@ def get_cost_center_target_details(filters):
|
|||||||
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
|
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
|
||||||
and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
|
and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
|
||||||
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
|
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
|
||||||
(filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True)
|
tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')),
|
||||||
|
as_dict=True)
|
||||||
|
|
||||||
|
|
||||||
#Get target distribution details of accounts of cost center
|
#Get target distribution details of accounts of cost center
|
||||||
@ -153,14 +150,14 @@ def get_actual_details(name, filters):
|
|||||||
|
|
||||||
return cc_actual_details
|
return cc_actual_details
|
||||||
|
|
||||||
def get_cost_center_account_month_map(filters):
|
def get_dimension_account_month_map(filters):
|
||||||
import datetime
|
import datetime
|
||||||
cost_center_target_details = get_cost_center_target_details(filters)
|
dimension_target_details = get_dimension_target_details(filters)
|
||||||
tdd = get_target_distribution_details(filters)
|
tdd = get_target_distribution_details(filters)
|
||||||
|
|
||||||
cam_map = {}
|
cam_map = {}
|
||||||
|
|
||||||
for ccd in cost_center_target_details:
|
for ccd in dimension_target_details:
|
||||||
actual_details = get_actual_details(ccd.budget_against, filters)
|
actual_details = get_actual_details(ccd.budget_against, filters)
|
||||||
|
|
||||||
for month_id in range(1, 13):
|
for month_id in range(1, 13):
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{%
|
{%
|
||||||
var report_columns = report.get_columns_for_print();
|
var report_columns = report.get_columns_for_print();
|
||||||
|
report_columns = report_columns.filter(col => !col.hidden);
|
||||||
|
|
||||||
if (report_columns.length > 8) {
|
if (report_columns.length > 8) {
|
||||||
frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application."));
|
frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application."));
|
||||||
@ -15,34 +16,35 @@
|
|||||||
height: 37px;
|
height: 37px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) %}
|
|
||||||
{% if(letterhead) { %}
|
|
||||||
<div style="margin-bottom: 7px;" class="text-center">
|
|
||||||
{%= frappe.boot.letter_heads[letterhead].header %}
|
|
||||||
</div>
|
|
||||||
{% } %}
|
|
||||||
<h2 class="text-center">{%= __(report.report_name) %}</h2>
|
<h2 class="text-center">{%= __(report.report_name) %}</h2>
|
||||||
<h3 class="text-center">{%= filters.company %}</h3>
|
<h3 class="text-center">{%= filters.company %}</h3>
|
||||||
|
|
||||||
{% if 'cost_center' in filters %}
|
{% if 'cost_center' in filters %}
|
||||||
<h3 class="text-center">{%= filters.cost_center %}</h3>
|
<h3 class="text-center">{%= filters.cost_center %}</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3 class="text-center">{%= filters.fiscal_year %}</h3>
|
<h3 class="text-center">{%= filters.fiscal_year %}</h3>
|
||||||
<h5 class="text-center">{%= __("Currency") %} : {%= filters.presentation_currency || erpnext.get_currency(filters.company) %} </h4>
|
<h5 class="text-center">
|
||||||
|
{%= __("Currency") %} : {%= filters.presentation_currency || erpnext.get_currency(filters.company) %}
|
||||||
|
</h5>
|
||||||
{% if (filters.from_date) { %}
|
{% if (filters.from_date) { %}
|
||||||
<h4 class="text-center">{%= frappe.datetime.str_to_user(filters.from_date) %} - {%= frappe.datetime.str_to_user(filters.to_date) %}</h3>
|
<h5 class="text-center">
|
||||||
|
{%= frappe.datetime.str_to_user(filters.from_date) %} - {%= frappe.datetime.str_to_user(filters.to_date) %}
|
||||||
|
</h5>
|
||||||
{% } %}
|
{% } %}
|
||||||
<hr>
|
<hr>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: {%= 100 - (report_columns.length - 2) * 13 %}%"></th>
|
<th style="width: {%= 100 - (report_columns.length - 1) * 13 %}%"></th>
|
||||||
{% for(var i=2, l=report_columns.length; i<l; i++) { %}
|
{% for (let i=1, l=report_columns.length; i<l; i++) { %}
|
||||||
<th class="text-right">{%= report_columns[i].label %}</th>
|
<th class="text-right">{%= report_columns[i].label %}</th>
|
||||||
{% } %}
|
{% } %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for(var j=0, k=data.length-1; j<k; j++) { %}
|
{% for(let j=0, k=data.length-1; j<k; j++) { %}
|
||||||
{%
|
{%
|
||||||
var row = data[j];
|
var row = data[j];
|
||||||
var row_class = data[j].parent_account ? "" : "financial-statements-important";
|
var row_class = data[j].parent_account ? "" : "financial-statements-important";
|
||||||
@ -52,11 +54,11 @@
|
|||||||
<td>
|
<td>
|
||||||
<span style="padding-left: {%= cint(data[j].indent) * 2 %}em">{%= row.account_name %}</span>
|
<span style="padding-left: {%= cint(data[j].indent) * 2 %}em">{%= row.account_name %}</span>
|
||||||
</td>
|
</td>
|
||||||
{% for(var i=2, l=report_columns.length; i<l; i++) { %}
|
{% for(let i=1, l=report_columns.length; i<l; i++) { %}
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% var fieldname = report_columns[i].fieldname; %}
|
{% const fieldname = report_columns[i].fieldname; %}
|
||||||
{% if (!is_null(row[fieldname])) { %}
|
{% if (!is_null(row[fieldname])) { %}
|
||||||
{%= format_currency(row[fieldname], filters.presentation_currency) %}
|
{%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
|
||||||
{% } %}
|
{% } %}
|
||||||
</td>
|
</td>
|
||||||
{% } %}
|
{% } %}
|
||||||
@ -64,4 +66,6 @@
|
|||||||
{% } %}
|
{% } %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
|
<p class="text-right text-muted">
|
||||||
|
Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}
|
||||||
|
</p>
|
||||||
|
@ -264,8 +264,8 @@ def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False
|
|||||||
|
|
||||||
def add_total_row(out, root_type, balance_must_be, period_list, company_currency):
|
def add_total_row(out, root_type, balance_must_be, period_list, company_currency):
|
||||||
total_row = {
|
total_row = {
|
||||||
"account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
|
"account_name": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
|
||||||
"account": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
|
"account": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
|
||||||
"currency": company_currency
|
"currency": company_currency
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for(var i=0, l=data.length-1; i<l; i++) { %}
|
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||||
<tr>
|
<tr>
|
||||||
{% if(data[i].posting_date) { %}
|
{% if(data[i].posting_date) { %}
|
||||||
<td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td>
|
<td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td>
|
||||||
|
@ -38,32 +38,46 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
|||||||
cost_center = list(set(invoice_cc_wh_map.get(inv.name, {}).get("cost_center", [])))
|
cost_center = list(set(invoice_cc_wh_map.get(inv.name, {}).get("cost_center", [])))
|
||||||
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
|
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
|
||||||
|
|
||||||
row = [
|
row = {
|
||||||
inv.name, inv.posting_date, inv.customer, inv.customer_name
|
'invoice': inv.name,
|
||||||
]
|
'posting_date': inv.posting_date,
|
||||||
|
'customer': inv.customer,
|
||||||
|
'customer_name': inv.customer_name
|
||||||
|
}
|
||||||
|
|
||||||
if additional_query_columns:
|
if additional_query_columns:
|
||||||
for col in additional_query_columns:
|
for col in additional_query_columns:
|
||||||
row.append(inv.get(col))
|
row.update({
|
||||||
|
col: inv.get(col)
|
||||||
|
})
|
||||||
|
|
||||||
|
row.update({
|
||||||
|
'customer_group': inv.get("customer_group"),
|
||||||
|
'territory': inv.get("territory"),
|
||||||
|
'tax_id': inv.get("tax_id"),
|
||||||
|
'receivable_account': inv.debit_to,
|
||||||
|
'mode_of_payment': ", ".join(mode_of_payments.get(inv.name, [])),
|
||||||
|
'project': inv.project,
|
||||||
|
'owner': inv.owner,
|
||||||
|
'remarks': inv.remarks,
|
||||||
|
'sales_order': ", ".join(sales_order),
|
||||||
|
'delivery_note': ", ".join(delivery_note),
|
||||||
|
'cost_center': ", ".join(cost_center),
|
||||||
|
'warehouse': ", ".join(warehouse),
|
||||||
|
'currency': company_currency
|
||||||
|
})
|
||||||
|
|
||||||
row +=[
|
|
||||||
inv.get("customer_group"),
|
|
||||||
inv.get("territory"),
|
|
||||||
inv.get("tax_id"),
|
|
||||||
inv.debit_to, ", ".join(mode_of_payments.get(inv.name, [])),
|
|
||||||
inv.project, inv.owner, inv.remarks,
|
|
||||||
", ".join(sales_order), ", ".join(delivery_note),", ".join(cost_center),
|
|
||||||
", ".join(warehouse), company_currency
|
|
||||||
]
|
|
||||||
# map income values
|
# map income values
|
||||||
base_net_total = 0
|
base_net_total = 0
|
||||||
for income_acc in income_accounts:
|
for income_acc in income_accounts:
|
||||||
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
|
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
|
||||||
base_net_total += income_amount
|
base_net_total += income_amount
|
||||||
row.append(income_amount)
|
row.update({
|
||||||
|
frappe.scrub(income_acc): income_amount
|
||||||
|
})
|
||||||
|
|
||||||
# net total
|
# net total
|
||||||
row.append(base_net_total or inv.base_net_total)
|
row.update({'net_total': base_net_total or inv.base_net_total})
|
||||||
|
|
||||||
# tax account
|
# tax account
|
||||||
total_tax = 0
|
total_tax = 0
|
||||||
@ -72,10 +86,18 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
|||||||
tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
|
tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
|
||||||
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
|
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
|
||||||
total_tax += tax_amount
|
total_tax += tax_amount
|
||||||
row.append(tax_amount)
|
row.update({
|
||||||
|
frappe.scrub(tax_acc): tax_amount
|
||||||
|
})
|
||||||
|
|
||||||
# total tax, grand total, outstanding amount & rounded total
|
# total tax, grand total, outstanding amount & rounded total
|
||||||
row += [total_tax, inv.base_grand_total, inv.base_rounded_total, inv.outstanding_amount]
|
|
||||||
|
row.update({
|
||||||
|
'tax_total': total_tax,
|
||||||
|
'grand_total': inv.base_grand_total,
|
||||||
|
'rounded_total': inv.base_rounded_total,
|
||||||
|
'outstanding_amount': inv.outstanding_amount
|
||||||
|
})
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
@ -84,19 +106,118 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
|||||||
def get_columns(invoice_list, additional_table_columns):
|
def get_columns(invoice_list, additional_table_columns):
|
||||||
"""return columns based on filters"""
|
"""return columns based on filters"""
|
||||||
columns = [
|
columns = [
|
||||||
_("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80",
|
{
|
||||||
_("Customer") + ":Link/Customer:120", _("Customer Name") + "::120"
|
'label': _("Invoice"),
|
||||||
|
'fieldname': 'invoice',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Sales Invoice',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Posting Date"),
|
||||||
|
'fieldname': 'posting_date',
|
||||||
|
'fieldtype': 'Date',
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Customer"),
|
||||||
|
'fieldname': 'customer',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Customer',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Customer Name"),
|
||||||
|
'fieldname': 'customer_name',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if additional_table_columns:
|
if additional_table_columns:
|
||||||
columns += additional_table_columns
|
columns += additional_table_columns
|
||||||
|
|
||||||
columns +=[
|
columns +=[
|
||||||
_("Customer Group") + ":Link/Customer Group:120", _("Territory") + ":Link/Territory:80",
|
{
|
||||||
_("Tax Id") + "::80", _("Receivable Account") + ":Link/Account:120", _("Mode of Payment") + "::120",
|
'label': _("Custmer Group"),
|
||||||
_("Project") +":Link/Project:80", _("Owner") + "::150", _("Remarks") + "::150",
|
'fieldname': 'customer_group',
|
||||||
_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
|
'fieldtype': 'Link',
|
||||||
_("Cost Center") + ":Link/Cost Center:100", _("Warehouse") + ":Link/Warehouse:100",
|
'options': 'Customer Group',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Territory"),
|
||||||
|
'fieldname': 'territory',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Territory',
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Tax Id"),
|
||||||
|
'fieldname': 'tax_id',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Receivable Account"),
|
||||||
|
'fieldname': 'receivable_account',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Account',
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Mode Of Payment"),
|
||||||
|
'fieldname': 'mode_of_payment',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Project"),
|
||||||
|
'fieldname': 'project',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'project',
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Owner"),
|
||||||
|
'fieldname': 'owner',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Remarks"),
|
||||||
|
'fieldname': 'remarks',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Sales Order"),
|
||||||
|
'fieldname': 'sales_order',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Sales Order',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Delivery Note"),
|
||||||
|
'fieldname': 'delivery_note',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Delivery Note',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Cost Center"),
|
||||||
|
'fieldname': 'cost_center',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Cost Center',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Warehouse"),
|
||||||
|
'fieldname': 'warehouse',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Warehouse',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "currency",
|
"fieldname": "currency",
|
||||||
"label": _("Currency"),
|
"label": _("Currency"),
|
||||||
@ -105,7 +226,10 @@ def get_columns(invoice_list, additional_table_columns):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
income_accounts = tax_accounts = income_columns = tax_columns = []
|
income_accounts = []
|
||||||
|
tax_accounts = []
|
||||||
|
income_columns = []
|
||||||
|
tax_columns = []
|
||||||
|
|
||||||
if invoice_list:
|
if invoice_list:
|
||||||
income_accounts = frappe.db.sql_list("""select distinct income_account
|
income_accounts = frappe.db.sql_list("""select distinct income_account
|
||||||
@ -119,14 +243,65 @@ def get_columns(invoice_list, additional_table_columns):
|
|||||||
and parent in (%s) order by account_head""" %
|
and parent in (%s) order by account_head""" %
|
||||||
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
|
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
|
||||||
|
|
||||||
income_columns = [(account + ":Currency/currency:120") for account in income_accounts]
|
for account in income_accounts:
|
||||||
|
income_columns.append({
|
||||||
|
"label": account,
|
||||||
|
"fieldname": frappe.scrub(account),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
})
|
||||||
|
|
||||||
for account in tax_accounts:
|
for account in tax_accounts:
|
||||||
if account not in income_accounts:
|
if account not in income_accounts:
|
||||||
tax_columns.append(account + ":Currency/currency:120")
|
tax_columns.append({
|
||||||
|
"label": account,
|
||||||
|
"fieldname": frappe.scrub(account),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
})
|
||||||
|
|
||||||
columns = columns + income_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
|
net_total_column = [{
|
||||||
[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
|
"label": _("Net Total"),
|
||||||
_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
|
"fieldname": "net_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
}]
|
||||||
|
|
||||||
|
total_columns = [
|
||||||
|
{
|
||||||
|
"label": _("Tax Total"),
|
||||||
|
"fieldname": "tax_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Grand Total"),
|
||||||
|
"fieldname": "grand_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Rounded Total"),
|
||||||
|
"fieldname": "rounded_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Outstanding Amount"),
|
||||||
|
"fieldname": "outstanding_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
columns = columns + income_columns + net_total_column + tax_columns + total_columns
|
||||||
|
|
||||||
return columns, income_accounts, tax_accounts
|
return columns, income_accounts, tax_accounts
|
||||||
|
|
||||||
|
@ -76,8 +76,7 @@ def get_data(filters):
|
|||||||
accumulate_values_into_parents(accounts, accounts_by_name)
|
accumulate_values_into_parents(accounts, accounts_by_name)
|
||||||
|
|
||||||
data = prepare_data(accounts, filters, total_row, parent_children_map, company_currency)
|
data = prepare_data(accounts, filters, total_row, parent_children_map, company_currency)
|
||||||
data = filter_out_zero_value_rows(data, parent_children_map,
|
data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values"))
|
||||||
show_zero_values=filters.get("show_zero_values"))
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -187,33 +186,11 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, filters,
|
|||||||
|
|
||||||
d["closing_debit"] = d["opening_debit"] + d["debit"]
|
d["closing_debit"] = d["opening_debit"] + d["debit"]
|
||||||
d["closing_credit"] = d["opening_credit"] + d["credit"]
|
d["closing_credit"] = d["opening_credit"] + d["credit"]
|
||||||
total_row["debit"] += d["debit"]
|
|
||||||
total_row["credit"] += d["credit"]
|
|
||||||
|
|
||||||
if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense":
|
prepare_opening_closing(d)
|
||||||
d["opening_debit"] -= d["opening_credit"]
|
|
||||||
d["closing_debit"] -= d["closing_credit"]
|
|
||||||
|
|
||||||
# For opening
|
for field in value_fields:
|
||||||
check_opening_closing_has_negative_value(d, "opening_debit", "opening_credit")
|
total_row[field] += d[field]
|
||||||
|
|
||||||
# For closing
|
|
||||||
check_opening_closing_has_negative_value(d, "closing_debit", "closing_credit")
|
|
||||||
|
|
||||||
if d["root_type"] == "Liability" or d["root_type"] == "Income":
|
|
||||||
d["opening_credit"] -= d["opening_debit"]
|
|
||||||
d["closing_credit"] -= d["closing_debit"]
|
|
||||||
|
|
||||||
# For opening
|
|
||||||
check_opening_closing_has_negative_value(d, "opening_credit", "opening_debit")
|
|
||||||
|
|
||||||
# For closing
|
|
||||||
check_opening_closing_has_negative_value(d, "closing_credit", "closing_debit")
|
|
||||||
|
|
||||||
total_row["opening_debit"] += d["opening_debit"]
|
|
||||||
total_row["closing_debit"] += d["closing_debit"]
|
|
||||||
total_row["opening_credit"] += d["opening_credit"]
|
|
||||||
total_row["closing_credit"] += d["closing_credit"]
|
|
||||||
|
|
||||||
return total_row
|
return total_row
|
||||||
|
|
||||||
@ -227,6 +204,10 @@ def prepare_data(accounts, filters, total_row, parent_children_map, company_curr
|
|||||||
data = []
|
data = []
|
||||||
|
|
||||||
for d in accounts:
|
for d in accounts:
|
||||||
|
# Prepare opening closing for group account
|
||||||
|
if parent_children_map.get(d.account):
|
||||||
|
prepare_opening_closing(d)
|
||||||
|
|
||||||
has_value = False
|
has_value = False
|
||||||
row = {
|
row = {
|
||||||
"account": d.name,
|
"account": d.name,
|
||||||
@ -313,11 +294,16 @@ def get_columns():
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def check_opening_closing_has_negative_value(d, dr_or_cr, switch_to_column):
|
def prepare_opening_closing(row):
|
||||||
# If opening debit has negetive value then move it to opening credit and vice versa.
|
dr_or_cr = "debit" if row["root_type"] in ["Asset", "Equity", "Expense"] else "credit"
|
||||||
|
reverse_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
|
||||||
|
|
||||||
if d[dr_or_cr] < 0:
|
for col_type in ["opening", "closing"]:
|
||||||
d[switch_to_column] = abs(d[dr_or_cr])
|
valid_col = col_type + "_" + dr_or_cr
|
||||||
d[dr_or_cr] = 0.0
|
reverse_col = col_type + "_" + reverse_dr_or_cr
|
||||||
else:
|
row[valid_col] -= row[reverse_col]
|
||||||
d[switch_to_column] = 0.0
|
if row[valid_col] < 0:
|
||||||
|
row[reverse_col] = abs(row[valid_col])
|
||||||
|
row[valid_col] = 0.0
|
||||||
|
else:
|
||||||
|
row[reverse_col] = 0.0
|
@ -65,6 +65,21 @@ frappe.query_reports["Trial Balance for Party"] = {
|
|||||||
return party_type;
|
return party_type;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "account",
|
||||||
|
"label": __("Account"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Account",
|
||||||
|
"get_query": function() {
|
||||||
|
var company = frappe.query_report.get_filter_value('company');
|
||||||
|
return {
|
||||||
|
"doctype": "Account",
|
||||||
|
"filters": {
|
||||||
|
"company": company,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "show_zero_values",
|
"fieldname": "show_zero_values",
|
||||||
"label": __("Show zero values"),
|
"label": __("Show zero values"),
|
||||||
|
@ -18,14 +18,17 @@ def execute(filters=None):
|
|||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_data(filters, show_party_name):
|
def get_data(filters, show_party_name):
|
||||||
party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
|
if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'):
|
||||||
if filters.get('party_type') == 'Student':
|
party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
|
||||||
|
elif filters.get('party_type') == 'Student':
|
||||||
party_name_field = 'first_name'
|
party_name_field = 'first_name'
|
||||||
elif filters.get('party_type') == 'Shareholder':
|
elif filters.get('party_type') == 'Shareholder':
|
||||||
party_name_field = 'title'
|
party_name_field = 'title'
|
||||||
|
else:
|
||||||
|
party_name_field = 'name'
|
||||||
|
|
||||||
party_filters = {"name": filters.get("party")} if filters.get("party") else {}
|
party_filters = {"name": filters.get("party")} if filters.get("party") else {}
|
||||||
parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
|
parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
|
||||||
filters = party_filters, order_by="name")
|
filters = party_filters, order_by="name")
|
||||||
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
||||||
opening_balances = get_opening_balances(filters)
|
opening_balances = get_opening_balances(filters)
|
||||||
@ -70,7 +73,7 @@ def get_data(filters, show_party_name):
|
|||||||
# totals
|
# totals
|
||||||
for col in total_row:
|
for col in total_row:
|
||||||
total_row[col] += row.get(col)
|
total_row[col] += row.get(col)
|
||||||
|
|
||||||
row.update({
|
row.update({
|
||||||
"currency": company_currency
|
"currency": company_currency
|
||||||
})
|
})
|
||||||
@ -78,7 +81,7 @@ def get_data(filters, show_party_name):
|
|||||||
has_value = False
|
has_value = False
|
||||||
if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
|
if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
|
||||||
has_value =True
|
has_value =True
|
||||||
|
|
||||||
if cint(filters.show_zero_values) or has_value:
|
if cint(filters.show_zero_values) or has_value:
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
@ -93,13 +96,19 @@ def get_data(filters, show_party_name):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def get_opening_balances(filters):
|
def get_opening_balances(filters):
|
||||||
|
|
||||||
|
account_filter = ''
|
||||||
|
if filters.get('account'):
|
||||||
|
account_filter = "and account = %s" % (frappe.db.escape(filters.get('account')))
|
||||||
|
|
||||||
gle = frappe.db.sql("""
|
gle = frappe.db.sql("""
|
||||||
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
|
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where company=%(company)s
|
where company=%(company)s
|
||||||
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
|
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
|
||||||
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
|
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
|
||||||
group by party""", {
|
{account_filter}
|
||||||
|
group by party""".format(account_filter=account_filter), {
|
||||||
"company": filters.company,
|
"company": filters.company,
|
||||||
"from_date": filters.from_date,
|
"from_date": filters.from_date,
|
||||||
"party_type": filters.party_type
|
"party_type": filters.party_type
|
||||||
@ -113,14 +122,20 @@ def get_opening_balances(filters):
|
|||||||
return opening
|
return opening
|
||||||
|
|
||||||
def get_balances_within_period(filters):
|
def get_balances_within_period(filters):
|
||||||
|
|
||||||
|
account_filter = ''
|
||||||
|
if filters.get('account'):
|
||||||
|
account_filter = "and account = %s" % (frappe.db.escape(filters.get('account')))
|
||||||
|
|
||||||
gle = frappe.db.sql("""
|
gle = frappe.db.sql("""
|
||||||
select party, sum(debit) as debit, sum(credit) as credit
|
select party, sum(debit) as debit, sum(credit) as credit
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where company=%(company)s
|
where company=%(company)s
|
||||||
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
|
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
|
||||||
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
|
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
|
||||||
and ifnull(is_opening, 'No') = 'No'
|
and ifnull(is_opening, 'No') = 'No'
|
||||||
group by party""", {
|
{account_filter}
|
||||||
|
group by party""".format(account_filter=account_filter), {
|
||||||
"company": filters.company,
|
"company": filters.company,
|
||||||
"from_date": filters.from_date,
|
"from_date": filters.from_date,
|
||||||
"to_date": filters.to_date,
|
"to_date": filters.to_date,
|
||||||
|
@ -13,6 +13,10 @@ from six import iteritems
|
|||||||
# imported to enable erpnext.accounts.utils.get_account_currency
|
# imported to enable erpnext.accounts.utils.get_account_currency
|
||||||
from erpnext.accounts.doctype.account.account import get_account_currency
|
from erpnext.accounts.doctype.account.account import get_account_currency
|
||||||
|
|
||||||
|
from erpnext.stock.utils import get_stock_value_on
|
||||||
|
from erpnext.stock import get_warehouse_account_map
|
||||||
|
|
||||||
|
|
||||||
class FiscalYearError(frappe.ValidationError): pass
|
class FiscalYearError(frappe.ValidationError): pass
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -560,23 +564,23 @@ def fix_total_debit_credit():
|
|||||||
(dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr),
|
(dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr),
|
||||||
(d.diff, d.voucher_type, d.voucher_no))
|
(d.diff, d.voucher_type, d.voucher_no))
|
||||||
|
|
||||||
def get_stock_and_account_difference(account_list=None, posting_date=None, company=None):
|
def get_stock_and_account_balance(account=None, posting_date=None, company=None):
|
||||||
from erpnext.stock.utils import get_stock_value_on
|
|
||||||
from erpnext.stock import get_warehouse_account_map
|
|
||||||
|
|
||||||
if not posting_date: posting_date = nowdate()
|
if not posting_date: posting_date = nowdate()
|
||||||
|
|
||||||
difference = {}
|
|
||||||
warehouse_account = get_warehouse_account_map(company)
|
warehouse_account = get_warehouse_account_map(company)
|
||||||
|
|
||||||
for warehouse, account_data in iteritems(warehouse_account):
|
account_balance = get_balance_on(account, posting_date, in_account_currency=False, ignore_account_permission=True)
|
||||||
if account_data.get('account') in account_list:
|
|
||||||
account_balance = get_balance_on(account_data.get('account'), posting_date, in_account_currency=False)
|
|
||||||
stock_value = get_stock_value_on(warehouse, posting_date)
|
|
||||||
if abs(flt(stock_value) - flt(account_balance)) > 0.005:
|
|
||||||
difference.setdefault(account_data.get('account'), flt(stock_value) - flt(account_balance))
|
|
||||||
|
|
||||||
return difference
|
related_warehouses = [wh for wh, wh_details in warehouse_account.items()
|
||||||
|
if wh_details.account == account and not wh_details.is_group]
|
||||||
|
|
||||||
|
total_stock_value = 0.0
|
||||||
|
for warehouse in related_warehouses:
|
||||||
|
value = get_stock_value_on(warehouse, posting_date)
|
||||||
|
total_stock_value += value
|
||||||
|
|
||||||
|
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
|
||||||
|
return flt(account_balance, precision), flt(total_stock_value, precision), related_warehouses
|
||||||
|
|
||||||
def get_currency_precision():
|
def get_currency_precision():
|
||||||
precision = cint(frappe.db.get_default("currency_precision"))
|
precision = cint(frappe.db.get_default("currency_precision"))
|
||||||
@ -626,7 +630,7 @@ def get_held_invoices(party_type, party):
|
|||||||
'select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()',
|
'select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()',
|
||||||
as_dict=1
|
as_dict=1
|
||||||
)
|
)
|
||||||
held_invoices = [d['name'] for d in held_invoices]
|
held_invoices = set([d['name'] for d in held_invoices])
|
||||||
|
|
||||||
return held_invoices
|
return held_invoices
|
||||||
|
|
||||||
@ -635,14 +639,19 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters
|
|||||||
outstanding_invoices = []
|
outstanding_invoices = []
|
||||||
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
|
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
|
||||||
|
|
||||||
if erpnext.get_party_account_type(party_type) == 'Receivable':
|
if account:
|
||||||
|
root_type = frappe.get_cached_value("Account", account, "root_type")
|
||||||
|
party_account_type = "Receivable" if root_type == "Asset" else "Payable"
|
||||||
|
else:
|
||||||
|
party_account_type = erpnext.get_party_account_type(party_type)
|
||||||
|
|
||||||
|
if party_account_type == 'Receivable':
|
||||||
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
|
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
|
||||||
payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
|
payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
|
||||||
else:
|
else:
|
||||||
dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
|
dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
|
||||||
payment_dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
|
payment_dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
|
||||||
|
|
||||||
invoice = 'Sales Invoice' if erpnext.get_party_account_type(party_type) == 'Receivable' else 'Purchase Invoice'
|
|
||||||
held_invoices = get_held_invoices(party_type, party)
|
held_invoices = get_held_invoices(party_type, party)
|
||||||
|
|
||||||
invoice_list = frappe.db.sql("""
|
invoice_list = frappe.db.sql("""
|
||||||
@ -661,7 +670,6 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters
|
|||||||
group by voucher_type, voucher_no
|
group by voucher_type, voucher_no
|
||||||
order by posting_date, name""".format(
|
order by posting_date, name""".format(
|
||||||
dr_or_cr=dr_or_cr,
|
dr_or_cr=dr_or_cr,
|
||||||
invoice = invoice,
|
|
||||||
condition=condition or ""
|
condition=condition or ""
|
||||||
), {
|
), {
|
||||||
"party_type": party_type,
|
"party_type": party_type,
|
||||||
|
@ -51,27 +51,25 @@ class CropCycle(Document):
|
|||||||
self.create_task(disease_doc.treatment_task, self.name, start_date)
|
self.create_task(disease_doc.treatment_task, self.name, start_date)
|
||||||
|
|
||||||
def create_project(self, period, crop_tasks):
|
def create_project(self, period, crop_tasks):
|
||||||
project = frappe.new_doc("Project")
|
project = frappe.get_doc({
|
||||||
project.update({
|
"doctype": "Project",
|
||||||
"project_name": self.title,
|
"project_name": self.title,
|
||||||
"expected_start_date": self.start_date,
|
"expected_start_date": self.start_date,
|
||||||
"expected_end_date": add_days(self.start_date, period - 1)
|
"expected_end_date": add_days(self.start_date, period - 1)
|
||||||
})
|
}).insert()
|
||||||
project.insert()
|
|
||||||
|
|
||||||
return project.name
|
return project.name
|
||||||
|
|
||||||
def create_task(self, crop_tasks, project_name, start_date):
|
def create_task(self, crop_tasks, project_name, start_date):
|
||||||
for crop_task in crop_tasks:
|
for crop_task in crop_tasks:
|
||||||
task = frappe.new_doc("Task")
|
frappe.get_doc({
|
||||||
task.update({
|
"doctype": "Task",
|
||||||
"subject": crop_task.get("task_name"),
|
"subject": crop_task.get("task_name"),
|
||||||
"priority": crop_task.get("priority"),
|
"priority": crop_task.get("priority"),
|
||||||
"project": project_name,
|
"project": project_name,
|
||||||
"exp_start_date": add_days(start_date, crop_task.get("start_day") - 1),
|
"exp_start_date": add_days(start_date, crop_task.get("start_day") - 1),
|
||||||
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
|
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
|
||||||
})
|
}).insert()
|
||||||
task.insert()
|
|
||||||
|
|
||||||
def reload_linked_analysis(self):
|
def reload_linked_analysis(self):
|
||||||
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
|
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
|
||||||
|
@ -41,6 +41,39 @@ frappe.ui.form.on('Asset', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setup: function(frm) {
|
||||||
|
frm.make_methods = {
|
||||||
|
'Asset Movement': () => {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
|
||||||
|
freeze: true,
|
||||||
|
args:{
|
||||||
|
"assets": [{ name: cur_frm.doc.name }]
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
if (r.message) {
|
||||||
|
var doc = frappe.model.sync(r.message)[0];
|
||||||
|
frappe.set_route("Form", doc.doctype, doc.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
frm.set_query("purchase_receipt", (doc) => {
|
||||||
|
return {
|
||||||
|
query: "erpnext.controllers.queries.get_purchase_receipts",
|
||||||
|
filters: { item_code: doc.item_code }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
frm.set_query("purchase_invoice", (doc) => {
|
||||||
|
return {
|
||||||
|
query: "erpnext.controllers.queries.get_purchase_invoices",
|
||||||
|
filters: { item_code: doc.item_code }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frappe.ui.form.trigger("Asset", "is_existing_asset");
|
frappe.ui.form.trigger("Asset", "is_existing_asset");
|
||||||
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
|
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
|
||||||
@ -78,11 +111,6 @@ frappe.ui.form.on('Asset', {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.status=='Submitted' && !frm.doc.is_existing_asset && !frm.doc.purchase_invoice) {
|
|
||||||
frm.add_custom_button(__("Purchase Invoice"), function() {
|
|
||||||
frm.trigger("make_purchase_invoice");
|
|
||||||
}, __('Create'));
|
|
||||||
}
|
|
||||||
if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) {
|
if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) {
|
||||||
frm.add_custom_button(__("Asset Maintenance"), function() {
|
frm.add_custom_button(__("Asset Maintenance"), function() {
|
||||||
frm.trigger("create_asset_maintenance");
|
frm.trigger("create_asset_maintenance");
|
||||||
@ -104,11 +132,40 @@ frappe.ui.form.on('Asset', {
|
|||||||
frm.trigger("setup_chart");
|
frm.trigger("setup_chart");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frm.trigger("toggle_reference_doc");
|
||||||
|
|
||||||
if (frm.doc.docstatus == 0) {
|
if (frm.doc.docstatus == 0) {
|
||||||
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggle_reference_doc: function(frm) {
|
||||||
|
if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) {
|
||||||
|
frm.set_df_property('purchase_invoice', 'read_only', 1);
|
||||||
|
frm.set_df_property('purchase_receipt', 'read_only', 1);
|
||||||
|
}
|
||||||
|
else if (frm.doc.is_existing_asset) {
|
||||||
|
frm.toggle_reqd('purchase_receipt', 0);
|
||||||
|
frm.toggle_reqd('purchase_invoice', 0);
|
||||||
|
}
|
||||||
|
else if (frm.doc.purchase_receipt) {
|
||||||
|
// if purchase receipt link is set then set PI disabled
|
||||||
|
frm.toggle_reqd('purchase_invoice', 0);
|
||||||
|
frm.set_df_property('purchase_invoice', 'read_only', 1);
|
||||||
|
}
|
||||||
|
else if (frm.doc.purchase_invoice) {
|
||||||
|
// if purchase invoice link is set then set PR disabled
|
||||||
|
frm.toggle_reqd('purchase_receipt', 0);
|
||||||
|
frm.set_df_property('purchase_receipt', 'read_only', 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frm.toggle_reqd('purchase_receipt', 1);
|
||||||
|
frm.set_df_property('purchase_receipt', 'read_only', 0);
|
||||||
|
frm.toggle_reqd('purchase_invoice', 1);
|
||||||
|
frm.set_df_property('purchase_invoice', 'read_only', 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
make_journal_entry: function(frm) {
|
make_journal_entry: function(frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.assets.doctype.asset.asset.make_journal_entry",
|
method: "erpnext.assets.doctype.asset.asset.make_journal_entry",
|
||||||
@ -176,21 +233,25 @@ frappe.ui.form.on('Asset', {
|
|||||||
|
|
||||||
item_code: function(frm) {
|
item_code: function(frm) {
|
||||||
if(frm.doc.item_code) {
|
if(frm.doc.item_code) {
|
||||||
frappe.call({
|
frm.trigger('set_finance_book');
|
||||||
method: "erpnext.assets.doctype.asset.asset.get_item_details",
|
|
||||||
args: {
|
|
||||||
item_code: frm.doc.item_code,
|
|
||||||
asset_category: frm.doc.asset_category
|
|
||||||
},
|
|
||||||
callback: function(r, rt) {
|
|
||||||
if(r.message) {
|
|
||||||
frm.set_value('finance_books', r.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
set_finance_book: function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.assets.doctype.asset.asset.get_item_details",
|
||||||
|
args: {
|
||||||
|
item_code: frm.doc.item_code,
|
||||||
|
asset_category: frm.doc.asset_category
|
||||||
|
},
|
||||||
|
callback: function(r, rt) {
|
||||||
|
if(r.message) {
|
||||||
|
frm.set_value('finance_books', r.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
available_for_use_date: function(frm) {
|
available_for_use_date: function(frm) {
|
||||||
$.each(frm.doc.finance_books || [], function(i, d) {
|
$.each(frm.doc.finance_books || [], function(i, d) {
|
||||||
if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date;
|
if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date;
|
||||||
@ -199,37 +260,23 @@ frappe.ui.form.on('Asset', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
is_existing_asset: function(frm) {
|
is_existing_asset: function(frm) {
|
||||||
|
frm.trigger("toggle_reference_doc");
|
||||||
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
|
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
|
||||||
},
|
},
|
||||||
|
|
||||||
opening_accumulated_depreciation: function(frm) {
|
opening_accumulated_depreciation: function(frm) {
|
||||||
erpnext.asset.set_accululated_depreciation(frm);
|
erpnext.asset.set_accumulated_depreciation(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
make_schedules_editable: function(frm) {
|
make_schedules_editable: function(frm) {
|
||||||
var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
|
if (frm.doc.finance_books) {
|
||||||
? true : false;
|
var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
|
||||||
|
? true : false;
|
||||||
|
|
||||||
frm.toggle_enable("schedules", is_editable);
|
frm.toggle_enable("schedules", is_editable);
|
||||||
frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
|
frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
|
||||||
frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable);
|
frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable);
|
||||||
},
|
}
|
||||||
|
|
||||||
make_purchase_invoice: function(frm) {
|
|
||||||
frappe.call({
|
|
||||||
args: {
|
|
||||||
"asset": frm.doc.name,
|
|
||||||
"item_code": frm.doc.item_code,
|
|
||||||
"gross_purchase_amount": frm.doc.gross_purchase_amount,
|
|
||||||
"company": frm.doc.company,
|
|
||||||
"posting_date": frm.doc.purchase_date
|
|
||||||
},
|
|
||||||
method: "erpnext.assets.doctype.asset.asset.make_purchase_invoice",
|
|
||||||
callback: function(r) {
|
|
||||||
var doclist = frappe.model.sync(r.message);
|
|
||||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
make_sales_invoice: function(frm) {
|
make_sales_invoice: function(frm) {
|
||||||
@ -282,17 +329,6 @@ frappe.ui.form.on('Asset', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
calculate_depreciation: function(frm) {
|
calculate_depreciation: function(frm) {
|
||||||
frappe.db.get_value("Asset Settings", {'name':"Asset Settings"}, 'schedule_based_on_fiscal_year', (data) => {
|
|
||||||
if (data.schedule_based_on_fiscal_year == 1) {
|
|
||||||
frm.set_df_property("depreciation_method", "options", "\nStraight Line\nManual");
|
|
||||||
frm.toggle_reqd("available_for_use_date", true);
|
|
||||||
frm.toggle_display("frequency_of_depreciation", false);
|
|
||||||
frappe.db.get_value("Fiscal Year", {'name': frappe.sys_defaults.fiscal_year}, "year_end_date", (data) => {
|
|
||||||
frm.set_value("next_depreciation_date", data.year_end_date);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -302,6 +338,65 @@ frappe.ui.form.on('Asset', {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
purchase_receipt: function(frm) {
|
||||||
|
frm.trigger('toggle_reference_doc');
|
||||||
|
|
||||||
|
if (frm.doc.purchase_receipt) {
|
||||||
|
if (frm.doc.item_code) {
|
||||||
|
frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => {
|
||||||
|
frm.set_value('company', pr_doc.company);
|
||||||
|
frm.set_value('purchase_date', pr_doc.posting_date);
|
||||||
|
const item = pr_doc.items.find(item => item.item_code === frm.doc.item_code);
|
||||||
|
if (!item) {
|
||||||
|
frm.set_value('purchase_receipt', '');
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Invalid Purchase Receipt'),
|
||||||
|
message: __("The selected Purchase Receipt doesn't contains selected Asset Item."),
|
||||||
|
indicator: 'red'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
frm.set_value('gross_purchase_amount', item.base_net_rate);
|
||||||
|
frm.set_value('location', item.asset_location);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frm.set_value('purchase_receipt', '');
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Not Allowed'),
|
||||||
|
message: __("Please select Item Code first")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
purchase_invoice: function(frm) {
|
||||||
|
frm.trigger('toggle_reference_doc');
|
||||||
|
if (frm.doc.purchase_invoice) {
|
||||||
|
if (frm.doc.item_code) {
|
||||||
|
frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => {
|
||||||
|
frm.set_value('company', pi_doc.company);
|
||||||
|
frm.set_value('purchase_date', pi_doc.posting_date);
|
||||||
|
const item = pi_doc.items.find(item => item.item_code === frm.doc.item_code);
|
||||||
|
if (!item) {
|
||||||
|
frm.set_value('purchase_invoice', '');
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Invalid Purchase Invoice'),
|
||||||
|
message: __("The selected Purchase Invoice doesn't contains selected Asset Item."),
|
||||||
|
indicator: 'red'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
frm.set_value('gross_purchase_amount', item.base_net_rate);
|
||||||
|
frm.set_value('location', item.asset_location);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frm.set_value('purchase_invoice', '');
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Not Allowed'),
|
||||||
|
message: __("Please select Item Code first")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
set_depreciation_rate: function(frm, row) {
|
set_depreciation_rate: function(frm, row) {
|
||||||
if (row.total_number_of_depreciations && row.frequency_of_depreciation
|
if (row.total_number_of_depreciations && row.frequency_of_depreciation
|
||||||
&& row.expected_value_after_useful_life) {
|
&& row.expected_value_after_useful_life) {
|
||||||
@ -371,12 +466,12 @@ frappe.ui.form.on('Depreciation Schedule', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
depreciation_amount: function(frm, cdt, cdn) {
|
depreciation_amount: function(frm, cdt, cdn) {
|
||||||
erpnext.asset.set_accululated_depreciation(frm);
|
erpnext.asset.set_accumulated_depreciation(frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
erpnext.asset.set_accululated_depreciation = function(frm) {
|
erpnext.asset.set_accumulated_depreciation = function(frm) {
|
||||||
if(frm.doc.depreciation_method != "Manual") return;
|
if(frm.doc.depreciation_method != "Manual") return;
|
||||||
|
|
||||||
var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
|
var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
|
||||||
@ -415,92 +510,19 @@ erpnext.asset.restore_asset = function(frm) {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
erpnext.asset.transfer_asset = function(frm) {
|
erpnext.asset.transfer_asset = function() {
|
||||||
var dialog = new frappe.ui.Dialog({
|
frappe.call({
|
||||||
title: __("Transfer Asset"),
|
method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
|
||||||
fields: [
|
freeze: true,
|
||||||
{
|
args:{
|
||||||
"label": __("Target Location"),
|
"assets": [{ name: cur_frm.doc.name }],
|
||||||
"fieldname": "target_location",
|
"purpose": "Transfer"
|
||||||
"fieldtype": "Link",
|
},
|
||||||
"options": "Location",
|
callback: function (r) {
|
||||||
"get_query": function () {
|
if (r.message) {
|
||||||
return {
|
var doc = frappe.model.sync(r.message)[0];
|
||||||
filters: [
|
frappe.set_route("Form", doc.doctype, doc.name);
|
||||||
["Location", "is_group", "=", 0]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": __("Select Serial No"),
|
|
||||||
"fieldname": "serial_nos",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Serial No",
|
|
||||||
"get_query": function () {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'asset': frm.doc.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"onchange": function() {
|
|
||||||
let val = this.get_value();
|
|
||||||
if (val) {
|
|
||||||
let serial_nos = dialog.get_value("serial_no") || val;
|
|
||||||
if (serial_nos) {
|
|
||||||
serial_nos = serial_nos.split('\n');
|
|
||||||
serial_nos.push(val);
|
|
||||||
|
|
||||||
const unique_sn = serial_nos.filter(function(elem, index, self) {
|
|
||||||
return index === self.indexOf(elem);
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog.set_value("serial_no", unique_sn.join('\n'));
|
|
||||||
dialog.set_value("serial_nos", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": __("Serial No"),
|
|
||||||
"fieldname": "serial_no",
|
|
||||||
"read_only": 1,
|
|
||||||
"fieldtype": "Small Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": __("Date"),
|
|
||||||
"fieldname": "transfer_date",
|
|
||||||
"fieldtype": "Datetime",
|
|
||||||
"reqd": 1,
|
|
||||||
"default": frappe.datetime.now_datetime()
|
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.set_primary_action(__("Transfer"), function() {
|
|
||||||
var args = dialog.get_values();
|
|
||||||
if(!args) return;
|
|
||||||
dialog.hide();
|
|
||||||
return frappe.call({
|
|
||||||
type: "GET",
|
|
||||||
method: "erpnext.assets.doctype.asset.asset.transfer_asset",
|
|
||||||
args: {
|
|
||||||
args: {
|
|
||||||
"asset": frm.doc.name,
|
|
||||||
"transaction_date": args.transfer_date,
|
|
||||||
"source_location": frm.doc.location,
|
|
||||||
"target_location": args.target_location,
|
|
||||||
"serial_no": args.serial_no,
|
|
||||||
"company": frm.doc.company
|
|
||||||
}
|
|
||||||
},
|
|
||||||
freeze: true,
|
|
||||||
callback: function(r) {
|
|
||||||
cur_frm.reload_doc();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext, math, json
|
import frappe, erpnext, math, json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from six import string_types
|
from six import string_types
|
||||||
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, add_days
|
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||||
from erpnext.assets.doctype.asset.depreciation \
|
from erpnext.assets.doctype.asset.depreciation \
|
||||||
@ -18,6 +18,7 @@ from erpnext.controllers.accounts_controller import AccountsController
|
|||||||
class Asset(AccountsController):
|
class Asset(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_asset_values()
|
self.validate_asset_values()
|
||||||
|
self.validate_asset_and_reference()
|
||||||
self.validate_item()
|
self.validate_item()
|
||||||
self.set_missing_values()
|
self.set_missing_values()
|
||||||
self.prepare_depreciation_data()
|
self.prepare_depreciation_data()
|
||||||
@ -29,10 +30,13 @@ class Asset(AccountsController):
|
|||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.validate_in_use_date()
|
self.validate_in_use_date()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.update_stock_movement()
|
self.make_asset_movement()
|
||||||
if not self.booked_fixed_asset and not is_cwip_accounting_disabled():
|
if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.asset_category):
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
|
def before_cancel(self):
|
||||||
|
self.cancel_auto_gen_movement()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.validate_cancellation()
|
self.validate_cancellation()
|
||||||
self.delete_depreciation_entries()
|
self.delete_depreciation_entries()
|
||||||
@ -40,6 +44,18 @@ class Asset(AccountsController):
|
|||||||
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
|
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
|
||||||
self.db_set('booked_fixed_asset', 0)
|
self.db_set('booked_fixed_asset', 0)
|
||||||
|
|
||||||
|
def validate_asset_and_reference(self):
|
||||||
|
if self.purchase_invoice or self.purchase_receipt:
|
||||||
|
reference_doc = 'Purchase Invoice' if self.purchase_invoice else 'Purchase Receipt'
|
||||||
|
reference_name = self.purchase_invoice or self.purchase_receipt
|
||||||
|
reference_doc = frappe.get_doc(reference_doc, reference_name)
|
||||||
|
if reference_doc.get('company') != self.company:
|
||||||
|
frappe.throw(_("Company of asset {0} and purchase document {1} doesn't matches.").format(self.name, reference_doc.get('name')))
|
||||||
|
|
||||||
|
|
||||||
|
if self.is_existing_asset and self.purchase_invoice:
|
||||||
|
frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
|
||||||
|
|
||||||
def prepare_depreciation_data(self):
|
def prepare_depreciation_data(self):
|
||||||
if self.calculate_depreciation:
|
if self.calculate_depreciation:
|
||||||
self.value_after_depreciation = 0
|
self.value_after_depreciation = 0
|
||||||
@ -76,10 +92,13 @@ class Asset(AccountsController):
|
|||||||
self.set('finance_books', finance_books)
|
self.set('finance_books', finance_books)
|
||||||
|
|
||||||
def validate_asset_values(self):
|
def validate_asset_values(self):
|
||||||
|
if not self.asset_category:
|
||||||
|
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
|
||||||
|
|
||||||
if not flt(self.gross_purchase_amount):
|
if not flt(self.gross_purchase_amount):
|
||||||
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
|
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
|
||||||
|
|
||||||
if not is_cwip_accounting_disabled():
|
if is_cwip_accounting_enabled(self.asset_category):
|
||||||
if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice):
|
if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice):
|
||||||
frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}").
|
frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}").
|
||||||
format(self.item_code))
|
format(self.item_code))
|
||||||
@ -105,6 +124,38 @@ class Asset(AccountsController):
|
|||||||
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
|
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
|
||||||
frappe.throw(_("Available-for-use Date should be after purchase date"))
|
frappe.throw(_("Available-for-use Date should be after purchase date"))
|
||||||
|
|
||||||
|
def cancel_auto_gen_movement(self):
|
||||||
|
movements = frappe.db.sql(
|
||||||
|
"""SELECT asm.name, asm.docstatus
|
||||||
|
FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
|
||||||
|
WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""", self.name, as_dict=1)
|
||||||
|
if len(movements) > 1:
|
||||||
|
frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \
|
||||||
|
cancelled manually to cancel this asset.'))
|
||||||
|
movement = frappe.get_doc('Asset Movement', movements[0].get('name'))
|
||||||
|
movement.flags.ignore_validate = True
|
||||||
|
movement.cancel()
|
||||||
|
|
||||||
|
def make_asset_movement(self):
|
||||||
|
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
|
||||||
|
reference_docname = self.purchase_receipt or self.purchase_invoice
|
||||||
|
assets = [{
|
||||||
|
'asset': self.name,
|
||||||
|
'asset_name': self.asset_name,
|
||||||
|
'target_location': self.location,
|
||||||
|
'to_employee': self.custodian
|
||||||
|
}]
|
||||||
|
asset_movement = frappe.get_doc({
|
||||||
|
'doctype': 'Asset Movement',
|
||||||
|
'assets': assets,
|
||||||
|
'purpose': 'Receipt',
|
||||||
|
'company': self.company,
|
||||||
|
'transaction_date': getdate(nowdate()),
|
||||||
|
'reference_doctype': reference_doctype,
|
||||||
|
'reference_name': reference_docname
|
||||||
|
}).insert()
|
||||||
|
asset_movement.submit()
|
||||||
|
|
||||||
def set_depreciation_rate(self):
|
def set_depreciation_rate(self):
|
||||||
for d in self.get("finance_books"):
|
for d in self.get("finance_books"):
|
||||||
d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
|
d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
|
||||||
@ -145,19 +196,31 @@ class Asset(AccountsController):
|
|||||||
schedule_date = add_months(d.depreciation_start_date,
|
schedule_date = add_months(d.depreciation_start_date,
|
||||||
n * cint(d.frequency_of_depreciation))
|
n * cint(d.frequency_of_depreciation))
|
||||||
|
|
||||||
|
# schedule date will be a year later from start date
|
||||||
|
# so monthly schedule date is calculated by removing 11 months from it
|
||||||
|
monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1)
|
||||||
|
|
||||||
# For first row
|
# For first row
|
||||||
if has_pro_rata and n==0:
|
if has_pro_rata and n==0:
|
||||||
depreciation_amount, days = get_pro_rata_amt(d, depreciation_amount,
|
depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount,
|
||||||
self.available_for_use_date, d.depreciation_start_date)
|
self.available_for_use_date, d.depreciation_start_date)
|
||||||
|
|
||||||
|
# For first depr schedule date will be the start date
|
||||||
|
# so monthly schedule date is calculated by removing month difference between use date and start date
|
||||||
|
monthly_schedule_date = add_months(d.depreciation_start_date, - months + 1)
|
||||||
|
|
||||||
# For last row
|
# For last row
|
||||||
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
|
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
|
||||||
to_date = add_months(self.available_for_use_date,
|
to_date = add_months(self.available_for_use_date,
|
||||||
n * cint(d.frequency_of_depreciation))
|
n * cint(d.frequency_of_depreciation))
|
||||||
|
|
||||||
depreciation_amount, days = get_pro_rata_amt(d,
|
depreciation_amount, days, months = get_pro_rata_amt(d,
|
||||||
depreciation_amount, schedule_date, to_date)
|
depreciation_amount, schedule_date, to_date)
|
||||||
|
|
||||||
|
monthly_schedule_date = add_months(schedule_date, 1)
|
||||||
|
|
||||||
schedule_date = add_days(schedule_date, days)
|
schedule_date = add_days(schedule_date, days)
|
||||||
|
last_schedule_date = schedule_date
|
||||||
|
|
||||||
if not depreciation_amount: continue
|
if not depreciation_amount: continue
|
||||||
value_after_depreciation -= flt(depreciation_amount,
|
value_after_depreciation -= flt(depreciation_amount,
|
||||||
@ -171,13 +234,50 @@ class Asset(AccountsController):
|
|||||||
skip_row = True
|
skip_row = True
|
||||||
|
|
||||||
if depreciation_amount > 0:
|
if depreciation_amount > 0:
|
||||||
self.append("schedules", {
|
# With monthly depreciation, each depreciation is divided by months remaining until next date
|
||||||
"schedule_date": schedule_date,
|
if self.allow_monthly_depreciation:
|
||||||
"depreciation_amount": depreciation_amount,
|
# month range is 1 to 12
|
||||||
"depreciation_method": d.depreciation_method,
|
# In pro rata case, for first and last depreciation, month range would be different
|
||||||
"finance_book": d.finance_book,
|
month_range = months \
|
||||||
"finance_book_id": d.idx
|
if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \
|
||||||
})
|
else d.frequency_of_depreciation
|
||||||
|
|
||||||
|
for r in range(month_range):
|
||||||
|
if (has_pro_rata and n == 0):
|
||||||
|
# For first entry of monthly depr
|
||||||
|
if r == 0:
|
||||||
|
days_until_first_depr = date_diff(monthly_schedule_date, self.available_for_use_date)
|
||||||
|
per_day_amt = depreciation_amount / days
|
||||||
|
depreciation_amount_for_current_month = per_day_amt * days_until_first_depr
|
||||||
|
depreciation_amount -= depreciation_amount_for_current_month
|
||||||
|
date = monthly_schedule_date
|
||||||
|
amount = depreciation_amount_for_current_month
|
||||||
|
else:
|
||||||
|
date = add_months(monthly_schedule_date, r)
|
||||||
|
amount = depreciation_amount / (month_range - 1)
|
||||||
|
elif (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) and r == cint(month_range) - 1:
|
||||||
|
# For last entry of monthly depr
|
||||||
|
date = last_schedule_date
|
||||||
|
amount = depreciation_amount / month_range
|
||||||
|
else:
|
||||||
|
date = add_months(monthly_schedule_date, r)
|
||||||
|
amount = depreciation_amount / month_range
|
||||||
|
|
||||||
|
self.append("schedules", {
|
||||||
|
"schedule_date": date,
|
||||||
|
"depreciation_amount": amount,
|
||||||
|
"depreciation_method": d.depreciation_method,
|
||||||
|
"finance_book": d.finance_book,
|
||||||
|
"finance_book_id": d.idx
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
self.append("schedules", {
|
||||||
|
"schedule_date": schedule_date,
|
||||||
|
"depreciation_amount": depreciation_amount,
|
||||||
|
"depreciation_method": d.depreciation_method,
|
||||||
|
"finance_book": d.finance_book,
|
||||||
|
"finance_book_id": d.idx
|
||||||
|
})
|
||||||
|
|
||||||
def check_is_pro_rata(self, row):
|
def check_is_pro_rata(self, row):
|
||||||
has_pro_rata = False
|
has_pro_rata = False
|
||||||
@ -196,7 +296,9 @@ class Asset(AccountsController):
|
|||||||
.format(row.idx))
|
.format(row.idx))
|
||||||
|
|
||||||
if not row.depreciation_start_date:
|
if not row.depreciation_start_date:
|
||||||
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
|
if not self.available_for_use_date:
|
||||||
|
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
|
||||||
|
row.depreciation_start_date = self.available_for_use_date
|
||||||
|
|
||||||
if not self.is_existing_asset:
|
if not self.is_existing_asset:
|
||||||
self.opening_accumulated_depreciation = 0
|
self.opening_accumulated_depreciation = 0
|
||||||
@ -345,22 +447,13 @@ class Asset(AccountsController):
|
|||||||
if d.finance_book == self.default_finance_book:
|
if d.finance_book == self.default_finance_book:
|
||||||
return cint(d.idx) - 1
|
return cint(d.idx) - 1
|
||||||
|
|
||||||
def update_stock_movement(self):
|
|
||||||
asset_movement = frappe.db.get_value('Asset Movement',
|
|
||||||
{'asset': self.name, 'reference_name': self.purchase_receipt, 'docstatus': 0}, 'name')
|
|
||||||
|
|
||||||
if asset_movement:
|
|
||||||
doc = frappe.get_doc('Asset Movement', asset_movement)
|
|
||||||
doc.naming_series = 'ACC-ASM-.YYYY.-'
|
|
||||||
doc.submit()
|
|
||||||
|
|
||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
|
||||||
if ((self.purchase_receipt or (self.purchase_invoice and
|
if ((self.purchase_receipt \
|
||||||
frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')))
|
or (self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')))
|
||||||
and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
||||||
fixed_aseet_account = get_asset_category_account(self.name, 'fixed_asset_account',
|
fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
|
||||||
asset_category = self.asset_category, company = self.company)
|
asset_category = self.asset_category, company = self.company)
|
||||||
|
|
||||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||||
@ -368,7 +461,7 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": cwip_account,
|
"account": cwip_account,
|
||||||
"against": fixed_aseet_account,
|
"against": fixed_asset_account,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"posting_date": self.available_for_use_date,
|
"posting_date": self.available_for_use_date,
|
||||||
"credit": self.purchase_receipt_amount,
|
"credit": self.purchase_receipt_amount,
|
||||||
@ -377,7 +470,7 @@ class Asset(AccountsController):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": fixed_aseet_account,
|
"account": fixed_asset_account,
|
||||||
"against": cwip_account,
|
"against": cwip_account,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"posting_date": self.available_for_use_date,
|
"posting_date": self.available_for_use_date,
|
||||||
@ -424,39 +517,23 @@ def update_maintenance_status():
|
|||||||
asset.set_status('Out of Order')
|
asset.set_status('Out of Order')
|
||||||
|
|
||||||
def make_post_gl_entry():
|
def make_post_gl_entry():
|
||||||
if is_cwip_accounting_disabled():
|
|
||||||
return
|
|
||||||
|
|
||||||
assets = frappe.db.sql_list(""" select name from `tabAsset`
|
asset_categories = frappe.db.get_all('Asset Category', fields = ['name', 'enable_cwip_accounting'])
|
||||||
where ifnull(booked_fixed_asset, 0) = 0 and available_for_use_date = %s""", nowdate())
|
|
||||||
|
|
||||||
for asset in assets:
|
for asset_category in asset_categories:
|
||||||
doc = frappe.get_doc('Asset', asset)
|
if cint(asset_category.enable_cwip_accounting):
|
||||||
doc.make_gl_entries()
|
assets = frappe.db.sql_list(""" select name from `tabAsset`
|
||||||
|
where asset_category = %s and ifnull(booked_fixed_asset, 0) = 0
|
||||||
|
and available_for_use_date = %s""", (asset_category.name, nowdate()))
|
||||||
|
|
||||||
|
for asset in assets:
|
||||||
|
doc = frappe.get_doc('Asset', asset)
|
||||||
|
doc.make_gl_entries()
|
||||||
|
|
||||||
def get_asset_naming_series():
|
def get_asset_naming_series():
|
||||||
meta = frappe.get_meta('Asset')
|
meta = frappe.get_meta('Asset')
|
||||||
return meta.get_field("naming_series").options
|
return meta.get_field("naming_series").options
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def make_purchase_invoice(asset, item_code, gross_purchase_amount, company, posting_date):
|
|
||||||
pi = frappe.new_doc("Purchase Invoice")
|
|
||||||
pi.company = company
|
|
||||||
pi.currency = frappe.get_cached_value('Company', company, "default_currency")
|
|
||||||
pi.set_posting_time = 1
|
|
||||||
pi.posting_date = posting_date
|
|
||||||
pi.append("items", {
|
|
||||||
"item_code": item_code,
|
|
||||||
"is_fixed_asset": 1,
|
|
||||||
"asset": asset,
|
|
||||||
"expense_account": get_asset_category_account(asset, 'fixed_asset_account'),
|
|
||||||
"qty": 1,
|
|
||||||
"price_list_rate": gross_purchase_amount,
|
|
||||||
"rate": gross_purchase_amount
|
|
||||||
})
|
|
||||||
pi.set_missing_values()
|
|
||||||
return pi
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_sales_invoice(asset, item_code, company, serial_no=None):
|
def make_sales_invoice(asset, item_code, company, serial_no=None):
|
||||||
si = frappe.new_doc("Sales Invoice")
|
si = frappe.new_doc("Sales Invoice")
|
||||||
@ -531,15 +608,21 @@ def get_item_details(item_code, asset_category):
|
|||||||
def get_asset_account(account_name, asset=None, asset_category=None, company=None):
|
def get_asset_account(account_name, asset=None, asset_category=None, company=None):
|
||||||
account = None
|
account = None
|
||||||
if asset:
|
if asset:
|
||||||
account = get_asset_category_account(asset, account_name,
|
account = get_asset_category_account(account_name, asset=asset,
|
||||||
asset_category = asset_category, company = company)
|
asset_category = asset_category, company = company)
|
||||||
|
|
||||||
|
if not asset and not account:
|
||||||
|
account = get_asset_category_account(account_name, asset_category = asset_category, company = company)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
account = frappe.get_cached_value('Company', company, account_name)
|
account = frappe.get_cached_value('Company', company, account_name)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
frappe.throw(_("Set {0} in asset category {1} or company {2}")
|
if not asset_category:
|
||||||
.format(account_name.replace('_', ' ').title(), asset_category, company))
|
frappe.throw(_("Set {0} in company {2}").format(account_name.replace('_', ' ').title(), company))
|
||||||
|
else:
|
||||||
|
frappe.throw(_("Set {0} in asset category {1} or company {2}")
|
||||||
|
.format(account_name.replace('_', ' ').title(), asset_category, company))
|
||||||
|
|
||||||
return account
|
return account
|
||||||
|
|
||||||
@ -574,17 +657,43 @@ def make_journal_entry(asset_name):
|
|||||||
|
|
||||||
return je
|
return je
|
||||||
|
|
||||||
def is_cwip_accounting_disabled():
|
@frappe.whitelist()
|
||||||
return cint(frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting"))
|
def make_asset_movement(assets, purpose=None):
|
||||||
|
import json
|
||||||
|
from six import string_types
|
||||||
|
|
||||||
|
if isinstance(assets, string_types):
|
||||||
|
assets = json.loads(assets)
|
||||||
|
|
||||||
|
if len(assets) == 0:
|
||||||
|
frappe.throw(_('Atleast one asset has to be selected.'))
|
||||||
|
|
||||||
|
asset_movement = frappe.new_doc("Asset Movement")
|
||||||
|
asset_movement.quantity = len(assets)
|
||||||
|
for asset in assets:
|
||||||
|
asset = frappe.get_doc('Asset', asset.get('name'))
|
||||||
|
asset_movement.company = asset.get('company')
|
||||||
|
asset_movement.append("assets", {
|
||||||
|
'asset': asset.get('name'),
|
||||||
|
'source_location': asset.get('location'),
|
||||||
|
'from_employee': asset.get('custodian')
|
||||||
|
})
|
||||||
|
|
||||||
|
if asset_movement.get('assets'):
|
||||||
|
return asset_movement.as_dict()
|
||||||
|
|
||||||
|
def is_cwip_accounting_enabled(asset_category):
|
||||||
|
return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting"))
|
||||||
|
|
||||||
def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
|
def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
|
||||||
days = date_diff(to_date, from_date)
|
days = date_diff(to_date, from_date)
|
||||||
|
months = month_diff(to_date, from_date)
|
||||||
total_days = get_total_days(to_date, row.frequency_of_depreciation)
|
total_days = get_total_days(to_date, row.frequency_of_depreciation)
|
||||||
|
|
||||||
return (depreciation_amount * flt(days)) / flt(total_days), days
|
return (depreciation_amount * flt(days)) / flt(total_days), days, months
|
||||||
|
|
||||||
def get_total_days(date, frequency):
|
def get_total_days(date, frequency):
|
||||||
period_start_date = add_months(date,
|
period_start_date = add_months(date,
|
||||||
cint(frequency) * -1)
|
cint(frequency) * -1)
|
||||||
|
|
||||||
return date_diff(date, period_start_date)
|
return date_diff(date, period_start_date)
|
||||||
|
@ -30,8 +30,24 @@ frappe.listview_settings['Asset'] = {
|
|||||||
|
|
||||||
} else if (doc.status === "Draft") {
|
} else if (doc.status === "Draft") {
|
||||||
return [__("Draft"), "red", "status,=,Draft"];
|
return [__("Draft"), "red", "status,=,Draft"];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onload: function(me) {
|
||||||
|
me.page.add_action_item('Make Asset Movement', function() {
|
||||||
|
const assets = me.get_checked_items();
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
|
||||||
|
freeze: true,
|
||||||
|
args:{
|
||||||
|
"assets": assets
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
if (r.message) {
|
||||||
|
var doc = frappe.model.sync(r.message)[0];
|
||||||
|
frappe.set_route("Form", doc.doctype, doc.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -7,14 +7,13 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import cstr, nowdate, getdate, flt, get_last_day, add_days, add_months
|
from frappe.utils import cstr, nowdate, getdate, flt, get_last_day, add_days, add_months
|
||||||
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
|
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
|
||||||
from erpnext.assets.doctype.asset.asset import make_sales_invoice, make_purchase_invoice
|
from erpnext.assets.doctype.asset.asset import make_sales_invoice
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_invoice
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_invoice
|
||||||
|
|
||||||
class TestAsset(unittest.TestCase):
|
class TestAsset(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
set_depreciation_settings_in_company()
|
set_depreciation_settings_in_company()
|
||||||
remove_prorated_depreciation_schedule()
|
|
||||||
create_asset_data()
|
create_asset_data()
|
||||||
frappe.db.sql("delete from `tabTax Rule`")
|
frappe.db.sql("delete from `tabTax Rule`")
|
||||||
|
|
||||||
@ -40,15 +39,15 @@ class TestAsset(unittest.TestCase):
|
|||||||
})
|
})
|
||||||
asset.submit()
|
asset.submit()
|
||||||
|
|
||||||
pi = make_purchase_invoice(asset.name, asset.item_code, asset.gross_purchase_amount,
|
pi = make_invoice(pr.name)
|
||||||
asset.company, asset.purchase_date)
|
|
||||||
pi.supplier = "_Test Supplier"
|
pi.supplier = "_Test Supplier"
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
asset.load_from_db()
|
asset.load_from_db()
|
||||||
self.assertEqual(asset.supplier, "_Test Supplier")
|
self.assertEqual(asset.supplier, "_Test Supplier")
|
||||||
self.assertEqual(asset.purchase_date, getdate(purchase_date))
|
self.assertEqual(asset.purchase_date, getdate(purchase_date))
|
||||||
self.assertEqual(asset.purchase_invoice, pi.name)
|
# Asset won't have reference to PI when purchased through PR
|
||||||
|
self.assertEqual(asset.purchase_receipt, pr.name)
|
||||||
|
|
||||||
expected_gle = (
|
expected_gle = (
|
||||||
("Asset Received But Not Billed - _TC", 100000.0, 0.0),
|
("Asset Received But Not Billed - _TC", 100000.0, 0.0),
|
||||||
@ -61,20 +60,23 @@ class TestAsset(unittest.TestCase):
|
|||||||
self.assertEqual(gle, expected_gle)
|
self.assertEqual(gle, expected_gle)
|
||||||
|
|
||||||
pi.cancel()
|
pi.cancel()
|
||||||
|
asset.cancel()
|
||||||
asset.load_from_db()
|
asset.load_from_db()
|
||||||
self.assertEqual(asset.supplier, None)
|
pr.load_from_db()
|
||||||
self.assertEqual(asset.purchase_invoice, None)
|
pr.cancel()
|
||||||
|
self.assertEqual(asset.docstatus, 2)
|
||||||
|
|
||||||
self.assertFalse(frappe.db.get_value("GL Entry",
|
self.assertFalse(frappe.db.get_value("GL Entry",
|
||||||
{"voucher_type": "Purchase Invoice", "voucher_no": pi.name}))
|
{"voucher_type": "Purchase Invoice", "voucher_no": pi.name}))
|
||||||
|
|
||||||
def test_is_fixed_asset_set(self):
|
def test_is_fixed_asset_set(self):
|
||||||
|
asset = create_asset(is_existing_asset = 1)
|
||||||
doc = frappe.new_doc('Purchase Invoice')
|
doc = frappe.new_doc('Purchase Invoice')
|
||||||
doc.supplier = '_Test Supplier'
|
doc.supplier = '_Test Supplier'
|
||||||
doc.append('items', {
|
doc.append('items', {
|
||||||
'item_code': 'Macbook Pro',
|
'item_code': 'Macbook Pro',
|
||||||
'qty': 1
|
'qty': 1,
|
||||||
|
'asset': asset.name
|
||||||
})
|
})
|
||||||
|
|
||||||
doc.set_missing_values()
|
doc.set_missing_values()
|
||||||
@ -200,7 +202,6 @@ class TestAsset(unittest.TestCase):
|
|||||||
self.assertEqual(schedules, expected_schedules)
|
self.assertEqual(schedules, expected_schedules)
|
||||||
|
|
||||||
def test_schedule_for_prorated_straight_line_method(self):
|
def test_schedule_for_prorated_straight_line_method(self):
|
||||||
set_prorated_depreciation_schedule()
|
|
||||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
qty=1, rate=100000.0, location="Test Location")
|
qty=1, rate=100000.0, location="Test Location")
|
||||||
|
|
||||||
@ -233,8 +234,6 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(schedules, expected_schedules)
|
self.assertEqual(schedules, expected_schedules)
|
||||||
|
|
||||||
remove_prorated_depreciation_schedule()
|
|
||||||
|
|
||||||
def test_depreciation(self):
|
def test_depreciation(self):
|
||||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
qty=1, rate=100000.0, location="Test Location")
|
qty=1, rate=100000.0, location="Test Location")
|
||||||
@ -484,9 +483,6 @@ class TestAsset(unittest.TestCase):
|
|||||||
self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
|
self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
|
||||||
|
|
||||||
def test_cwip_accounting(self):
|
def test_cwip_accounting(self):
|
||||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
|
||||||
make_purchase_invoice as make_purchase_invoice_from_pr)
|
|
||||||
|
|
||||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
qty=1, rate=5000, do_not_submit=True, location="Test Location")
|
qty=1, rate=5000, do_not_submit=True, location="Test Location")
|
||||||
|
|
||||||
@ -515,13 +511,13 @@ class TestAsset(unittest.TestCase):
|
|||||||
("CWIP Account - _TC", 5250.0, 0.0)
|
("CWIP Account - _TC", 5250.0, 0.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||||
where voucher_type='Purchase Receipt' and voucher_no = %s
|
where voucher_type='Purchase Receipt' and voucher_no = %s
|
||||||
order by account""", pr.name)
|
order by account""", pr.name)
|
||||||
|
|
||||||
self.assertEqual(gle, expected_gle)
|
self.assertEqual(pr_gle, expected_gle)
|
||||||
|
|
||||||
pi = make_purchase_invoice_from_pr(pr.name)
|
pi = make_invoice(pr.name)
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
|
||||||
expected_gle = (
|
expected_gle = (
|
||||||
@ -532,11 +528,11 @@ class TestAsset(unittest.TestCase):
|
|||||||
("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
|
("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
|
||||||
)
|
)
|
||||||
|
|
||||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||||
where voucher_type='Purchase Invoice' and voucher_no = %s
|
where voucher_type='Purchase Invoice' and voucher_no = %s
|
||||||
order by account""", pi.name)
|
order by account""", pi.name)
|
||||||
|
|
||||||
self.assertEqual(gle, expected_gle)
|
self.assertEqual(pi_gle, expected_gle)
|
||||||
|
|
||||||
asset = frappe.db.get_value('Asset',
|
asset = frappe.db.get_value('Asset',
|
||||||
{'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
|
{'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
|
||||||
@ -565,6 +561,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
where voucher_type='Asset' and voucher_no = %s
|
where voucher_type='Asset' and voucher_no = %s
|
||||||
order by account""", asset_doc.name)
|
order by account""", asset_doc.name)
|
||||||
|
|
||||||
|
|
||||||
self.assertEqual(gle, expected_gle)
|
self.assertEqual(gle, expected_gle)
|
||||||
|
|
||||||
def test_expense_head(self):
|
def test_expense_head(self):
|
||||||
@ -575,7 +572,6 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
|
self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
|
||||||
|
|
||||||
|
|
||||||
def create_asset_data():
|
def create_asset_data():
|
||||||
if not frappe.db.exists("Asset Category", "Computers"):
|
if not frappe.db.exists("Asset Category", "Computers"):
|
||||||
create_asset_category()
|
create_asset_category()
|
||||||
@ -596,15 +592,15 @@ def create_asset(**args):
|
|||||||
|
|
||||||
asset = frappe.get_doc({
|
asset = frappe.get_doc({
|
||||||
"doctype": "Asset",
|
"doctype": "Asset",
|
||||||
"asset_name": "Macbook Pro 1",
|
"asset_name": args.asset_name or "Macbook Pro 1",
|
||||||
"asset_category": "Computers",
|
"asset_category": "Computers",
|
||||||
"item_code": "Macbook Pro",
|
"item_code": args.item_code or "Macbook Pro",
|
||||||
"company": "_Test Company",
|
"company": args.company or"_Test Company",
|
||||||
"purchase_date": "2015-01-01",
|
"purchase_date": "2015-01-01",
|
||||||
"calculate_depreciation": 0,
|
"calculate_depreciation": 0,
|
||||||
"gross_purchase_amount": 100000,
|
"gross_purchase_amount": 100000,
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"warehouse": "_Test Warehouse - _TC",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"available_for_use_date": "2020-06-06",
|
"available_for_use_date": "2020-06-06",
|
||||||
"location": "Test Location",
|
"location": "Test Location",
|
||||||
"asset_owner": "Company",
|
"asset_owner": "Company",
|
||||||
@ -616,6 +612,9 @@ def create_asset(**args):
|
|||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if args.submit:
|
||||||
|
asset.submit()
|
||||||
|
|
||||||
return asset
|
return asset
|
||||||
|
|
||||||
def create_asset_category():
|
def create_asset_category():
|
||||||
@ -623,6 +622,7 @@ def create_asset_category():
|
|||||||
asset_category.asset_category_name = "Computers"
|
asset_category.asset_category_name = "Computers"
|
||||||
asset_category.total_number_of_depreciations = 3
|
asset_category.total_number_of_depreciations = 3
|
||||||
asset_category.frequency_of_depreciation = 3
|
asset_category.frequency_of_depreciation = 3
|
||||||
|
asset_category.enable_cwip_accounting = 1
|
||||||
asset_category.append("accounts", {
|
asset_category.append("accounts", {
|
||||||
"company_name": "_Test Company",
|
"company_name": "_Test Company",
|
||||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||||
@ -632,6 +632,8 @@ def create_asset_category():
|
|||||||
asset_category.insert()
|
asset_category.insert()
|
||||||
|
|
||||||
def create_fixed_asset_item():
|
def create_fixed_asset_item():
|
||||||
|
meta = frappe.get_meta('Asset')
|
||||||
|
naming_series = meta.get_field("naming_series").options.splitlines()[0] or 'ACC-ASS-.YYYY.-'
|
||||||
try:
|
try:
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Item",
|
"doctype": "Item",
|
||||||
@ -642,7 +644,9 @@ def create_fixed_asset_item():
|
|||||||
"item_group": "All Item Groups",
|
"item_group": "All Item Groups",
|
||||||
"stock_uom": "Nos",
|
"stock_uom": "Nos",
|
||||||
"is_stock_item": 0,
|
"is_stock_item": 0,
|
||||||
"is_fixed_asset": 1
|
"is_fixed_asset": 1,
|
||||||
|
"auto_create_assets": 1,
|
||||||
|
"asset_naming_series": naming_series
|
||||||
}).insert()
|
}).insert()
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
@ -656,19 +660,4 @@ def set_depreciation_settings_in_company():
|
|||||||
company.save()
|
company.save()
|
||||||
|
|
||||||
# Enable booking asset depreciation entry automatically
|
# Enable booking asset depreciation entry automatically
|
||||||
frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
|
frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
|
||||||
|
|
||||||
def remove_prorated_depreciation_schedule():
|
|
||||||
asset_settings = frappe.get_doc("Asset Settings", "Asset Settings")
|
|
||||||
asset_settings.schedule_based_on_fiscal_year = 0
|
|
||||||
asset_settings.save()
|
|
||||||
|
|
||||||
frappe.db.commit()
|
|
||||||
|
|
||||||
def set_prorated_depreciation_schedule():
|
|
||||||
asset_settings = frappe.get_doc("Asset Settings", "Asset Settings")
|
|
||||||
asset_settings.schedule_based_on_fiscal_year = 1
|
|
||||||
asset_settings.number_of_days_in_fiscal_year = 360
|
|
||||||
asset_settings.save()
|
|
||||||
|
|
||||||
frappe.db.commit()
|
|
@ -1,284 +1,115 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_import": 1,
|
||||||
"allow_guest_to_view": 0,
|
"allow_rename": 1,
|
||||||
"allow_import": 1,
|
"autoname": "field:asset_category_name",
|
||||||
"allow_rename": 1,
|
"creation": "2016-03-01 17:41:39.778765",
|
||||||
"autoname": "field:asset_category_name",
|
"doctype": "DocType",
|
||||||
"beta": 0,
|
"document_type": "Document",
|
||||||
"creation": "2016-03-01 17:41:39.778765",
|
"engine": "InnoDB",
|
||||||
"custom": 0,
|
"field_order": [
|
||||||
"docstatus": 0,
|
"asset_category_name",
|
||||||
"doctype": "DocType",
|
"column_break_3",
|
||||||
"document_type": "Document",
|
"depreciation_options",
|
||||||
"editable_grid": 0,
|
"enable_cwip_accounting",
|
||||||
"engine": "InnoDB",
|
"finance_book_detail",
|
||||||
|
"finance_books",
|
||||||
|
"section_break_2",
|
||||||
|
"accounts"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "asset_category_name",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Data",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Asset Category Name",
|
||||||
"columns": 0,
|
"reqd": 1,
|
||||||
"fieldname": "asset_category_name",
|
"unique": 1
|
||||||
"fieldtype": "Data",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Asset Category Name",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_3",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Column Break"
|
||||||
"bold": 0,
|
},
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_3",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "finance_book_detail",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Section Break",
|
||||||
"bold": 0,
|
"label": "Finance Book Detail"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "finance_book_detail",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Finance Book Detail",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "finance_books",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Table",
|
||||||
"bold": 0,
|
"label": "Finance Books",
|
||||||
"collapsible": 0,
|
"options": "Asset Finance Book"
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "finance_books",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Finance Books",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Asset Finance Book",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "section_break_2",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Section Break",
|
||||||
"bold": 0,
|
"label": "Accounts"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_2",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Accounts",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "accounts",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Table",
|
||||||
"bold": 0,
|
"label": "Accounts",
|
||||||
"collapsible": 0,
|
"options": "Asset Category Account",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fieldname": "accounts",
|
},
|
||||||
"fieldtype": "Table",
|
{
|
||||||
"hidden": 0,
|
"fieldname": "depreciation_options",
|
||||||
"ignore_user_permissions": 0,
|
"fieldtype": "Section Break",
|
||||||
"ignore_xss_filter": 0,
|
"label": "Depreciation Options"
|
||||||
"in_filter": 0,
|
},
|
||||||
"in_global_search": 0,
|
{
|
||||||
"in_list_view": 0,
|
"default": "0",
|
||||||
"in_standard_filter": 0,
|
"fieldname": "enable_cwip_accounting",
|
||||||
"label": "Accounts",
|
"fieldtype": "Check",
|
||||||
"length": 0,
|
"label": "Enable Capital Work in Progress Accounting"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Asset Category Account",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"modified": "2019-10-11 12:19:59.759136",
|
||||||
"hide_heading": 0,
|
"modified_by": "Administrator",
|
||||||
"hide_toolbar": 0,
|
"module": "Assets",
|
||||||
"idx": 0,
|
"name": "Asset Category",
|
||||||
"image_view": 0,
|
"owner": "Administrator",
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-05-12 14:56:04.116425",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Assets",
|
|
||||||
"name": "Asset Category",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"import": 1,
|
||||||
"email": 1,
|
"print": 1,
|
||||||
"export": 1,
|
"read": 1,
|
||||||
"if_owner": 0,
|
"report": 1,
|
||||||
"import": 1,
|
"role": "Accounts User",
|
||||||
"permlevel": 0,
|
"share": 1,
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"import": 1,
|
||||||
"email": 1,
|
"print": 1,
|
||||||
"export": 1,
|
"read": 1,
|
||||||
"if_owner": 0,
|
"report": 1,
|
||||||
"import": 1,
|
"role": "Accounts Manager",
|
||||||
"permlevel": 0,
|
"share": 1,
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"print": 1,
|
||||||
"email": 1,
|
"read": 1,
|
||||||
"export": 1,
|
"report": 1,
|
||||||
"if_owner": 0,
|
"role": "Quality Manager",
|
||||||
"import": 0,
|
"share": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Quality Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"show_name_in_global_search": 1,
|
||||||
"read_only": 0,
|
"sort_field": "modified",
|
||||||
"read_only_onload": 0,
|
"sort_order": "DESC"
|
||||||
"show_name_in_global_search": 1,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -10,20 +10,27 @@ from frappe.model.document import Document
|
|||||||
|
|
||||||
class AssetCategory(Document):
|
class AssetCategory(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.validate_finance_books()
|
||||||
|
|
||||||
|
def validate_finance_books(self):
|
||||||
for d in self.finance_books:
|
for d in self.finance_books:
|
||||||
for field in ("Total Number of Depreciations", "Frequency of Depreciation"):
|
for field in ("Total Number of Depreciations", "Frequency of Depreciation"):
|
||||||
if cint(d.get(frappe.scrub(field)))<1:
|
if cint(d.get(frappe.scrub(field)))<1:
|
||||||
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
|
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_asset_category_account(asset, fieldname, account=None, asset_category = None, company = None):
|
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
|
||||||
if not asset_category and company:
|
if item and frappe.db.get_value("Item", item, "is_fixed_asset"):
|
||||||
|
asset_category = frappe.db.get_value("Item", item, ["asset_category"])
|
||||||
|
|
||||||
|
elif not asset_category or not company:
|
||||||
if account:
|
if account:
|
||||||
if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
|
if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
|
||||||
account=None
|
account=None
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
asset_category, company = frappe.db.get_value("Asset", asset, ["asset_category", "company"])
|
asset_details = frappe.db.get_value("Asset", asset, ["asset_category", "company"])
|
||||||
|
asset_category, company = asset_details or [None, None]
|
||||||
|
|
||||||
account = frappe.db.get_value("Asset Category Account",
|
account = frappe.db.get_value("Asset Category Account",
|
||||||
filters={"parent": asset_category, "company_name": company}, fieldname=fieldname)
|
filters={"parent": asset_category, "company_name": company}, fieldname=fieldname)
|
||||||
|
@ -73,8 +73,10 @@ def create_asset_data():
|
|||||||
'doctype': 'Location',
|
'doctype': 'Location',
|
||||||
'location_name': 'Test Location'
|
'location_name': 'Test Location'
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
if not frappe.db.exists("Item", "Photocopier"):
|
if not frappe.db.exists("Item", "Photocopier"):
|
||||||
|
meta = frappe.get_meta('Asset')
|
||||||
|
naming_series = meta.get_field("naming_series").options
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Item",
|
"doctype": "Item",
|
||||||
"item_code": "Photocopier",
|
"item_code": "Photocopier",
|
||||||
@ -83,7 +85,9 @@ def create_asset_data():
|
|||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"is_fixed_asset": 1,
|
"is_fixed_asset": 1,
|
||||||
"is_stock_item": 0,
|
"is_stock_item": 0,
|
||||||
"asset_category": "Equipment"
|
"asset_category": "Equipment",
|
||||||
|
"auto_create_assets": 1,
|
||||||
|
"asset_naming_series": naming_series
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
def create_maintenance_team():
|
def create_maintenance_team():
|
||||||
|
@ -2,27 +2,101 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Asset Movement', {
|
frappe.ui.form.on('Asset Movement', {
|
||||||
select_serial_no: function(frm) {
|
setup: (frm) => {
|
||||||
if (frm.doc.select_serial_no) {
|
frm.set_query("to_employee", "assets", (doc) => {
|
||||||
let serial_no = frm.doc.serial_no
|
|
||||||
? frm.doc.serial_no + '\n' + frm.doc.select_serial_no : frm.doc.select_serial_no;
|
|
||||||
frm.set_value("serial_no", serial_no);
|
|
||||||
frm.set_value("quantity", serial_no.split('\n').length);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
serial_no: function(frm) {
|
|
||||||
const qty = frm.doc.serial_no ? frm.doc.serial_no.split('\n').length : 0;
|
|
||||||
frm.set_value("quantity", qty);
|
|
||||||
},
|
|
||||||
|
|
||||||
setup: function(frm) {
|
|
||||||
frm.set_query("select_serial_no", function() {
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"asset": frm.doc.asset
|
company: doc.company
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
})
|
||||||
|
frm.set_query("from_employee", "assets", (doc) => {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: doc.company
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
frm.set_query("reference_name", (doc) => {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: doc.company,
|
||||||
|
docstatus: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
frm.set_query("reference_doctype", () => {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
name: ["in", ["Purchase Receipt", "Purchase Invoice"]]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
frm.set_query("asset", "assets", () => {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
status: ["not in", ["Draft"]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onload: (frm) => {
|
||||||
|
frm.trigger('set_required_fields');
|
||||||
|
},
|
||||||
|
|
||||||
|
purpose: (frm) => {
|
||||||
|
frm.trigger('set_required_fields');
|
||||||
|
},
|
||||||
|
|
||||||
|
set_required_fields: (frm, cdt, cdn) => {
|
||||||
|
let fieldnames_to_be_altered;
|
||||||
|
if (frm.doc.purpose === 'Transfer') {
|
||||||
|
fieldnames_to_be_altered = {
|
||||||
|
target_location: { read_only: 0, reqd: 1 },
|
||||||
|
source_location: { read_only: 1, reqd: 1 },
|
||||||
|
from_employee: { read_only: 1, reqd: 0 },
|
||||||
|
to_employee: { read_only: 1, reqd: 0 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (frm.doc.purpose === 'Receipt') {
|
||||||
|
fieldnames_to_be_altered = {
|
||||||
|
target_location: { read_only: 0, reqd: 1 },
|
||||||
|
source_location: { read_only: 1, reqd: 0 },
|
||||||
|
from_employee: { read_only: 0, reqd: 1 },
|
||||||
|
to_employee: { read_only: 1, reqd: 0 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (frm.doc.purpose === 'Issue') {
|
||||||
|
fieldnames_to_be_altered = {
|
||||||
|
target_location: { read_only: 1, reqd: 0 },
|
||||||
|
source_location: { read_only: 1, reqd: 1 },
|
||||||
|
from_employee: { read_only: 1, reqd: 0 },
|
||||||
|
to_employee: { read_only: 0, reqd: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Object.keys(fieldnames_to_be_altered).forEach(fieldname => {
|
||||||
|
let property_to_be_altered = fieldnames_to_be_altered[fieldname];
|
||||||
|
Object.keys(property_to_be_altered).forEach(property => {
|
||||||
|
let value = property_to_be_altered[property];
|
||||||
|
frm.set_df_property(fieldname, property, value, cdn, 'assets');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
frm.refresh_field('assets');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on('Asset Movement Item', {
|
||||||
|
asset: function(frm, cdt, cdn) {
|
||||||
|
// on manual entry of an asset auto sets their source location / employee
|
||||||
|
const asset_name = locals[cdt][cdn].asset;
|
||||||
|
if (asset_name){
|
||||||
|
frappe.db.get_doc('Asset', asset_name).then((asset_doc) => {
|
||||||
|
if(asset_doc.location) frappe.model.set_value(cdt, cdn, 'source_location', asset_doc.location);
|
||||||
|
if(asset_doc.custodian) frappe.model.set_value(cdt, cdn, 'from_employee', asset_doc.custodian);
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err); // eslint-disable-line
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -1,26 +1,19 @@
|
|||||||
{
|
{
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "format:ACC-ASM-{YYYY}-{#####}",
|
||||||
"creation": "2016-04-25 18:00:23.559973",
|
"creation": "2016-04-25 18:00:23.559973",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"naming_series",
|
|
||||||
"company",
|
"company",
|
||||||
"purpose",
|
"purpose",
|
||||||
"asset",
|
|
||||||
"transaction_date",
|
|
||||||
"column_break_4",
|
"column_break_4",
|
||||||
"quantity",
|
"transaction_date",
|
||||||
"select_serial_no",
|
"section_break_10",
|
||||||
"serial_no",
|
"assets",
|
||||||
"section_break_7",
|
|
||||||
"source_location",
|
|
||||||
"target_location",
|
|
||||||
"column_break_10",
|
|
||||||
"from_employee",
|
|
||||||
"to_employee",
|
|
||||||
"reference",
|
"reference",
|
||||||
"reference_doctype",
|
"reference_doctype",
|
||||||
|
"column_break_9",
|
||||||
"reference_name",
|
"reference_name",
|
||||||
"amended_from"
|
"amended_from"
|
||||||
],
|
],
|
||||||
@ -36,23 +29,12 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Transfer",
|
|
||||||
"fieldname": "purpose",
|
"fieldname": "purpose",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Purpose",
|
"label": "Purpose",
|
||||||
"options": "\nIssue\nReceipt\nTransfer",
|
"options": "\nIssue\nReceipt\nTransfer",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "asset",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_global_search": 1,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Asset",
|
|
||||||
"options": "Asset",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "transaction_date",
|
"fieldname": "transaction_date",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
@ -65,56 +47,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "quantity",
|
"collapsible": 1,
|
||||||
"fieldtype": "Float",
|
|
||||||
"label": "Quantity"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "select_serial_no",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Select Serial No",
|
|
||||||
"options": "Serial No"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "serial_no",
|
|
||||||
"fieldtype": "Small Text",
|
|
||||||
"label": "Serial No"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "section_break_7",
|
|
||||||
"fieldtype": "Section Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "source_location",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Source Location",
|
|
||||||
"options": "Location"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "target_location",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Target Location",
|
|
||||||
"options": "Location"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_10",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "from_employee",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"ignore_user_permissions": 1,
|
|
||||||
"label": "From Employee",
|
|
||||||
"options": "Employee"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "to_employee",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"ignore_user_permissions": 1,
|
|
||||||
"label": "To Employee",
|
|
||||||
"options": "Employee"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "reference",
|
"fieldname": "reference",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Reference"
|
"label": "Reference"
|
||||||
@ -122,18 +55,16 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "reference_doctype",
|
"fieldname": "reference_doctype",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Reference DocType",
|
"label": "Reference Document Type",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "DocType",
|
"options": "DocType"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "reference_name",
|
"fieldname": "reference_name",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"label": "Reference Name",
|
"label": "Reference Document Name",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "reference_doctype",
|
"options": "reference_doctype"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
@ -145,16 +76,23 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "ACC-ASM-.YYYY.-",
|
"fieldname": "section_break_10",
|
||||||
"fieldname": "naming_series",
|
"fieldtype": "Section Break"
|
||||||
"fieldtype": "Select",
|
},
|
||||||
"label": "Series",
|
{
|
||||||
"options": "ACC-ASM-.YYYY.-",
|
"fieldname": "assets",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Assets",
|
||||||
|
"options": "Asset Movement Item",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_9",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-09-16 16:27:53.887634",
|
"modified": "2019-11-23 13:28:47.256935",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Movement",
|
"name": "Asset Movement",
|
||||||
|
@ -5,101 +5,142 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class AssetMovement(Document):
|
class AssetMovement(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_asset()
|
self.validate_asset()
|
||||||
self.validate_location()
|
self.validate_location()
|
||||||
|
self.validate_employee()
|
||||||
|
|
||||||
def validate_asset(self):
|
def validate_asset(self):
|
||||||
status, company = frappe.db.get_value("Asset", self.asset, ["status", "company"])
|
for d in self.assets:
|
||||||
if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
|
status, company = frappe.db.get_value("Asset", d.asset, ["status", "company"])
|
||||||
frappe.throw(_("{0} asset cannot be transferred").format(status))
|
if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
|
||||||
|
frappe.throw(_("{0} asset cannot be transferred").format(status))
|
||||||
|
|
||||||
if company != self.company:
|
if company != self.company:
|
||||||
frappe.throw(_("Asset {0} does not belong to company {1}").format(self.asset, self.company))
|
frappe.throw(_("Asset {0} does not belong to company {1}").format(d.asset, self.company))
|
||||||
|
|
||||||
if self.serial_no and len(get_serial_nos(self.serial_no)) != self.quantity:
|
if not (d.source_location or d.target_location or d.from_employee or d.to_employee):
|
||||||
frappe.throw(_("Number of serial nos and quantity must be the same"))
|
frappe.throw(_("Either location or employee must be required"))
|
||||||
|
|
||||||
if not(self.source_location or self.target_location or self.from_employee or self.to_employee):
|
|
||||||
frappe.throw(_("Either location or employee must be required"))
|
|
||||||
|
|
||||||
if (not self.serial_no and
|
|
||||||
frappe.db.get_value('Serial No', {'asset': self.asset}, 'name')):
|
|
||||||
frappe.throw(_("Serial no is required for the asset {0}").format(self.asset))
|
|
||||||
|
|
||||||
def validate_location(self):
|
def validate_location(self):
|
||||||
if self.purpose in ['Transfer', 'Issue']:
|
for d in self.assets:
|
||||||
if not self.serial_no and not (self.from_employee or self.to_employee):
|
if self.purpose in ['Transfer', 'Issue']:
|
||||||
self.source_location = frappe.db.get_value("Asset", self.asset, "location")
|
if not d.source_location:
|
||||||
|
d.source_location = frappe.db.get_value("Asset", d.asset, "location")
|
||||||
|
|
||||||
if self.purpose == 'Issue' and not (self.source_location or self.from_employee):
|
if not d.source_location:
|
||||||
frappe.throw(_("Source Location is required for the asset {0}").format(self.asset))
|
frappe.throw(_("Source Location is required for the Asset {0}").format(d.asset))
|
||||||
|
|
||||||
if self.serial_no and self.source_location:
|
if d.source_location:
|
||||||
s_nos = get_serial_nos(self.serial_no)
|
current_location = frappe.db.get_value("Asset", d.asset, "location")
|
||||||
serial_nos = frappe.db.sql_list(""" select name from `tabSerial No` where location != '%s'
|
|
||||||
and name in (%s)""" %(self.source_location, ','.join(['%s'] * len(s_nos))), tuple(s_nos))
|
|
||||||
|
|
||||||
if serial_nos:
|
if current_location != d.source_location:
|
||||||
frappe.throw(_("Serial nos {0} does not belongs to the location {1}").
|
frappe.throw(_("Asset {0} does not belongs to the location {1}").
|
||||||
format(','.join(serial_nos), self.source_location))
|
format(d.asset, d.source_location))
|
||||||
|
|
||||||
|
if self.purpose == 'Issue':
|
||||||
|
if d.target_location:
|
||||||
|
frappe.throw(_("Issuing cannot be done to a location. \
|
||||||
|
Please enter employee who has issued Asset {0}").format(d.asset), title="Incorrect Movement Purpose")
|
||||||
|
if not d.to_employee:
|
||||||
|
frappe.throw(_("Employee is required while issuing Asset {0}").format(d.asset))
|
||||||
|
|
||||||
|
if self.purpose == 'Transfer':
|
||||||
|
if d.to_employee:
|
||||||
|
frappe.throw(_("Transferring cannot be done to an Employee. \
|
||||||
|
Please enter location where Asset {0} has to be transferred").format(
|
||||||
|
d.asset), title="Incorrect Movement Purpose")
|
||||||
|
if not d.target_location:
|
||||||
|
frappe.throw(_("Target Location is required while transferring Asset {0}").format(d.asset))
|
||||||
|
if d.source_location == d.target_location:
|
||||||
|
frappe.throw(_("Source and Target Location cannot be same"))
|
||||||
|
|
||||||
|
if self.purpose == 'Receipt':
|
||||||
|
# only when asset is bought and first entry is made
|
||||||
|
if not d.source_location and not (d.target_location or d.to_employee):
|
||||||
|
frappe.throw(_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset))
|
||||||
|
elif d.source_location:
|
||||||
|
# when asset is received from an employee
|
||||||
|
if d.target_location and not d.from_employee:
|
||||||
|
frappe.throw(_("From employee is required while receiving Asset {0} to a target location").format(d.asset))
|
||||||
|
if d.from_employee and not d.target_location:
|
||||||
|
frappe.throw(_("Target Location is required while receiving Asset {0} from an employee").format(d.asset))
|
||||||
|
if d.to_employee and d.target_location:
|
||||||
|
frappe.throw(_("Asset {0} cannot be received at a location and \
|
||||||
|
given to employee in a single movement").format(d.asset))
|
||||||
|
|
||||||
if self.source_location and self.source_location == self.target_location and self.purpose == 'Transfer':
|
def validate_employee(self):
|
||||||
frappe.throw(_("Source and Target Location cannot be same"))
|
for d in self.assets:
|
||||||
|
if d.from_employee:
|
||||||
|
current_custodian = frappe.db.get_value("Asset", d.asset, "custodian")
|
||||||
|
|
||||||
if self.purpose == 'Receipt' and not (self.target_location or self.to_employee):
|
if current_custodian != d.from_employee:
|
||||||
frappe.throw(_("Target Location is required for the asset {0}").format(self.asset))
|
frappe.throw(_("Asset {0} does not belongs to the custodian {1}").
|
||||||
|
format(d.asset, d.from_employee))
|
||||||
|
|
||||||
|
if d.to_employee and frappe.db.get_value("Employee", d.to_employee, "company") != self.company:
|
||||||
|
frappe.throw(_("Employee {0} does not belongs to the company {1}").
|
||||||
|
format(d.to_employee, self.company))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.set_latest_location_in_asset()
|
self.set_latest_location_in_asset()
|
||||||
|
|
||||||
|
def before_cancel(self):
|
||||||
|
self.validate_last_movement()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.set_latest_location_in_asset()
|
self.set_latest_location_in_asset()
|
||||||
|
|
||||||
|
def validate_last_movement(self):
|
||||||
|
for d in self.assets:
|
||||||
|
auto_gen_movement_entry = frappe.db.sql(
|
||||||
|
"""
|
||||||
|
SELECT asm.name
|
||||||
|
FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm
|
||||||
|
WHERE
|
||||||
|
asm.docstatus=1 and
|
||||||
|
asm_item.parent=asm.name and
|
||||||
|
asm_item.asset=%s and
|
||||||
|
asm.company=%s and
|
||||||
|
asm_item.source_location is NULL and
|
||||||
|
asm.purpose=%s
|
||||||
|
ORDER BY
|
||||||
|
asm.transaction_date asc
|
||||||
|
""", (d.asset, self.company, 'Receipt'), as_dict=1)
|
||||||
|
if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name:
|
||||||
|
frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
|
||||||
|
auto generated for Asset {1}').format(self.name, d.asset))
|
||||||
|
|
||||||
def set_latest_location_in_asset(self):
|
def set_latest_location_in_asset(self):
|
||||||
location, employee = '', ''
|
current_location, current_employee = '', ''
|
||||||
cond = "1=1"
|
cond = "1=1"
|
||||||
|
|
||||||
args = {
|
for d in self.assets:
|
||||||
'asset': self.asset,
|
args = {
|
||||||
'company': self.company
|
'asset': d.asset,
|
||||||
}
|
'company': self.company
|
||||||
|
}
|
||||||
|
|
||||||
if self.serial_no:
|
# latest entry corresponds to current document's location, employee when transaction date > previous dates
|
||||||
cond = "serial_no like %(txt)s"
|
# In case of cancellation it corresponds to previous latest document's location, employee
|
||||||
args.update({
|
latest_movement_entry = frappe.db.sql(
|
||||||
'txt': "%%%s%%" % self.serial_no
|
"""
|
||||||
})
|
SELECT asm_item.target_location, asm_item.to_employee
|
||||||
|
FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm
|
||||||
|
WHERE
|
||||||
|
asm_item.parent=asm.name and
|
||||||
|
asm_item.asset=%(asset)s and
|
||||||
|
asm.company=%(company)s and
|
||||||
|
asm.docstatus=1 and {0}
|
||||||
|
ORDER BY
|
||||||
|
asm.transaction_date desc limit 1
|
||||||
|
""".format(cond), args)
|
||||||
|
if latest_movement_entry:
|
||||||
|
current_location = latest_movement_entry[0][0]
|
||||||
|
current_employee = latest_movement_entry[0][1]
|
||||||
|
|
||||||
latest_movement_entry = frappe.db.sql("""select target_location, to_employee from `tabAsset Movement`
|
frappe.db.set_value('Asset', d.asset, 'location', current_location)
|
||||||
where asset=%(asset)s and docstatus=1 and company=%(company)s and {0}
|
frappe.db.set_value('Asset', d.asset, 'custodian', current_employee)
|
||||||
order by transaction_date desc limit 1""".format(cond), args)
|
|
||||||
|
|
||||||
if latest_movement_entry:
|
|
||||||
location = latest_movement_entry[0][0]
|
|
||||||
employee = latest_movement_entry[0][1]
|
|
||||||
elif self.purpose in ['Transfer', 'Receipt']:
|
|
||||||
movement_entry = frappe.db.sql("""select source_location, from_employee from `tabAsset Movement`
|
|
||||||
where asset=%(asset)s and docstatus=2 and company=%(company)s and {0}
|
|
||||||
order by transaction_date asc limit 1""".format(cond), args)
|
|
||||||
if movement_entry:
|
|
||||||
location = movement_entry[0][0]
|
|
||||||
employee = movement_entry[0][1]
|
|
||||||
|
|
||||||
if not self.serial_no:
|
|
||||||
frappe.db.set_value("Asset", self.asset, "location", location)
|
|
||||||
|
|
||||||
if not employee and self.purpose in ['Receipt', 'Transfer']:
|
|
||||||
employee = self.to_employee
|
|
||||||
|
|
||||||
if self.serial_no:
|
|
||||||
for d in get_serial_nos(self.serial_no):
|
|
||||||
if (location or (self.purpose == 'Issue' and self.source_location)):
|
|
||||||
frappe.db.set_value('Serial No', d, 'location', location)
|
|
||||||
|
|
||||||
if employee or self.docstatus==2 or self.purpose == 'Issue':
|
|
||||||
frappe.db.set_value('Serial No', d, 'employee', employee)
|
|
||||||
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
import erpnext
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from frappe.utils import now, nowdate, get_last_day, add_days
|
from frappe.utils import now, nowdate, get_last_day, add_days
|
||||||
from erpnext.assets.doctype.asset.test_asset import create_asset_data
|
from erpnext.assets.doctype.asset.test_asset import create_asset_data
|
||||||
@ -16,7 +17,6 @@ class TestAssetMovement(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
create_asset_data()
|
create_asset_data()
|
||||||
make_location()
|
make_location()
|
||||||
make_serialized_item()
|
|
||||||
|
|
||||||
def test_movement(self):
|
def test_movement(self):
|
||||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
@ -38,68 +38,72 @@ class TestAssetMovement(unittest.TestCase):
|
|||||||
|
|
||||||
if asset.docstatus == 0:
|
if asset.docstatus == 0:
|
||||||
asset.submit()
|
asset.submit()
|
||||||
|
|
||||||
|
# check asset movement is created
|
||||||
if not frappe.db.exists("Location", "Test Location 2"):
|
if not frappe.db.exists("Location", "Test Location 2"):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
'doctype': 'Location',
|
'doctype': 'Location',
|
||||||
'location_name': 'Test Location 2'
|
'location_name': 'Test Location 2'
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
movement1 = create_asset_movement(asset= asset.name, purpose = 'Transfer',
|
movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||||
company=asset.company, source_location="Test Location", target_location="Test Location 2")
|
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
|
||||||
|
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
|
||||||
|
|
||||||
movement2 = create_asset_movement(asset= asset.name, purpose = 'Transfer',
|
movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||||
company=asset.company, source_location = "Test Location 2", target_location="Test Location")
|
assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}],
|
||||||
|
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||||
|
|
||||||
movement1.cancel()
|
movement1.cancel()
|
||||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||||
|
|
||||||
movement2.cancel()
|
employee = make_employee("testassetmovemp@example.com", company="_Test Company")
|
||||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
movement3 = create_asset_movement(purpose = 'Issue', company = asset.company,
|
||||||
|
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}],
|
||||||
def test_movement_for_serialized_asset(self):
|
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||||
asset_item = "Test Serialized Asset Item"
|
|
||||||
pr = make_purchase_receipt(item_code=asset_item, rate = 1000, qty=3, location = "Mumbai")
|
# after issuing asset should belong to an employee not at a location
|
||||||
asset_name = frappe.db.get_value('Asset', {'purchase_receipt': pr.name}, 'name')
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None)
|
||||||
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee)
|
||||||
|
|
||||||
|
def test_last_movement_cancellation(self):
|
||||||
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
|
qty=1, rate=100000.0, location="Test Location")
|
||||||
|
|
||||||
|
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||||
asset = frappe.get_doc('Asset', asset_name)
|
asset = frappe.get_doc('Asset', asset_name)
|
||||||
month_end_date = get_last_day(nowdate())
|
|
||||||
asset.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
|
|
||||||
|
|
||||||
asset.calculate_depreciation = 1
|
asset.calculate_depreciation = 1
|
||||||
|
asset.available_for_use_date = '2020-06-06'
|
||||||
|
asset.purchase_date = '2020-06-06'
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 200,
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"next_depreciation_date": "2020-12-31",
|
||||||
"depreciation_method": "Straight Line",
|
"depreciation_method": "Straight Line",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 3,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 10,
|
||||||
"depreciation_start_date": month_end_date
|
"depreciation_start_date": "2020-06-06"
|
||||||
})
|
})
|
||||||
asset.submit()
|
if asset.docstatus == 0:
|
||||||
serial_nos = frappe.db.get_value('Asset Movement', {'reference_name': pr.name}, 'serial_no')
|
asset.submit()
|
||||||
|
|
||||||
|
if not frappe.db.exists("Location", "Test Location 2"):
|
||||||
|
frappe.get_doc({
|
||||||
|
'doctype': 'Location',
|
||||||
|
'location_name': 'Test Location 2'
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name })
|
||||||
|
self.assertRaises(frappe.ValidationError, movement.cancel)
|
||||||
|
|
||||||
mov1 = create_asset_movement(asset=asset_name, purpose = 'Transfer',
|
movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||||
company=asset.company, source_location = "Mumbai", target_location="Pune", serial_no=serial_nos)
|
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
|
||||||
self.assertEqual(mov1.target_location, "Pune")
|
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||||
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
|
||||||
|
|
||||||
serial_no = frappe.db.get_value('Serial No', {'asset': asset_name}, 'name')
|
movement1.cancel()
|
||||||
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||||
employee = make_employee("testassetemp@example.com")
|
|
||||||
create_asset_movement(asset=asset_name, purpose = 'Transfer',
|
|
||||||
company=asset.company, serial_no=serial_no, to_employee=employee)
|
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'employee'), employee)
|
|
||||||
|
|
||||||
create_asset_movement(asset=asset_name, purpose = 'Transfer', company=asset.company,
|
|
||||||
serial_no=serial_no, from_employee=employee, to_employee="_T-Employee-00001")
|
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'location'), "Pune")
|
|
||||||
|
|
||||||
mov4 = create_asset_movement(asset=asset_name, purpose = 'Transfer',
|
|
||||||
company=asset.company, source_location = "Pune", target_location="Nagpur", serial_no=serial_nos)
|
|
||||||
self.assertEqual(mov4.target_location, "Nagpur")
|
|
||||||
self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'location'), "Nagpur")
|
|
||||||
self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'employee'), "_T-Employee-00001")
|
|
||||||
|
|
||||||
def create_asset_movement(**args):
|
def create_asset_movement(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
@ -109,22 +113,14 @@ def create_asset_movement(**args):
|
|||||||
|
|
||||||
movement = frappe.new_doc("Asset Movement")
|
movement = frappe.new_doc("Asset Movement")
|
||||||
movement.update({
|
movement.update({
|
||||||
"asset": args.asset,
|
"assets": args.assets,
|
||||||
"transaction_date": args.transaction_date,
|
"transaction_date": args.transaction_date,
|
||||||
"target_location": args.target_location,
|
|
||||||
"company": args.company,
|
"company": args.company,
|
||||||
'purpose': args.purpose or 'Receipt',
|
'purpose': args.purpose or 'Receipt',
|
||||||
'serial_no': args.serial_no,
|
'reference_doctype': args.reference_doctype,
|
||||||
'quantity': len(get_serial_nos(args.serial_no)) if args.serial_no else 1,
|
'reference_name': args.reference_name
|
||||||
'from_employee': "_T-Employee-00001" or args.from_employee,
|
|
||||||
'to_employee': args.to_employee
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if args.source_location:
|
|
||||||
movement.update({
|
|
||||||
'source_location': args.source_location
|
|
||||||
})
|
|
||||||
|
|
||||||
movement.insert()
|
movement.insert()
|
||||||
movement.submit()
|
movement.submit()
|
||||||
|
|
||||||
@ -137,33 +133,3 @@ def make_location():
|
|||||||
'doctype': 'Location',
|
'doctype': 'Location',
|
||||||
'location_name': location
|
'location_name': location
|
||||||
}).insert(ignore_permissions = True)
|
}).insert(ignore_permissions = True)
|
||||||
|
|
||||||
def make_serialized_item():
|
|
||||||
asset_item = "Test Serialized Asset Item"
|
|
||||||
|
|
||||||
if not frappe.db.exists('Item', asset_item):
|
|
||||||
asset_category = frappe.get_all('Asset Category')
|
|
||||||
|
|
||||||
if asset_category:
|
|
||||||
asset_category = asset_category[0].name
|
|
||||||
|
|
||||||
if not asset_category:
|
|
||||||
doc = frappe.get_doc({
|
|
||||||
'doctype': 'Asset Category',
|
|
||||||
'asset_category_name': 'Test Asset Category',
|
|
||||||
'depreciation_method': 'Straight Line',
|
|
||||||
'total_number_of_depreciations': 12,
|
|
||||||
'frequency_of_depreciation': 1,
|
|
||||||
'accounts': [{
|
|
||||||
'company_name': '_Test Company',
|
|
||||||
'fixed_asset_account': '_Test Fixed Asset - _TC',
|
|
||||||
'accumulated_depreciation_account': 'Depreciation - _TC',
|
|
||||||
'depreciation_expense_account': 'Depreciation - _TC'
|
|
||||||
}]
|
|
||||||
}).insert()
|
|
||||||
|
|
||||||
asset_category = doc.name
|
|
||||||
|
|
||||||
make_item(asset_item, {'is_stock_item':0,
|
|
||||||
'stock_uom': 'Box', 'is_fixed_asset': 1, 'has_serial_no': 1,
|
|
||||||
'asset_category': asset_category, 'serial_no_series': 'ABC.###'})
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user