Merge branch 'develop' into account-add-rename
This commit is contained in:
commit
25f4c1636e
@ -16,7 +16,7 @@
|
|||||||
ERPNext as a monolith includes the following areas for managing businesses:
|
ERPNext as a monolith includes the following areas for managing businesses:
|
||||||
|
|
||||||
1. [Accounting](https://erpnext.com/open-source-accounting)
|
1. [Accounting](https://erpnext.com/open-source-accounting)
|
||||||
1. [Inventory](https://erpnext.com/distribution/inventory-management-system)
|
1. [Warehouse Management](https://erpnext.com/distribution/warehouse-management-system)
|
||||||
1. [CRM](https://erpnext.com/open-source-crm)
|
1. [CRM](https://erpnext.com/open-source-crm)
|
||||||
1. [Sales](https://erpnext.com/open-source-sales-purchase)
|
1. [Sales](https://erpnext.com/open-source-sales-purchase)
|
||||||
1. [Purchase](https://erpnext.com/open-source-sales-purchase)
|
1. [Purchase](https://erpnext.com/open-source-sales-purchase)
|
||||||
|
@ -98,7 +98,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Accounting",
|
"label": "Accounting",
|
||||||
"modified": "2020-06-19 12:42:44.054598",
|
"modified": "2020-09-09 11:45:33.766400",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounting",
|
"name": "Accounting",
|
||||||
@ -147,15 +147,10 @@
|
|||||||
"link_to": "Trial Balance",
|
"link_to": "Trial Balance",
|
||||||
"type": "Report"
|
"type": "Report"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "Point of Sale",
|
|
||||||
"link_to": "point-of-sale",
|
|
||||||
"type": "Page"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "Dashboard",
|
"label": "Dashboard",
|
||||||
"link_to": "Accounts",
|
"link_to": "Accounts",
|
||||||
"type": "Dashboard"
|
"type": "Dashboard"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -55,7 +55,7 @@ class BankStatementTransactionEntry(Document):
|
|||||||
|
|
||||||
def populate_payment_entries(self):
|
def populate_payment_entries(self):
|
||||||
if self.bank_statement is None: return
|
if self.bank_statement is None: return
|
||||||
filename = self.bank_statement.split("/")[-1]
|
file_url = self.bank_statement
|
||||||
if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
|
if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
|
||||||
frappe.throw(_("Transactions already retreived from the statement"))
|
frappe.throw(_("Transactions already retreived from the statement"))
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ class BankStatementTransactionEntry(Document):
|
|||||||
if self.bank_settings:
|
if self.bank_settings:
|
||||||
mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items
|
mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items
|
||||||
statement_headers = self.get_statement_headers()
|
statement_headers = self.get_statement_headers()
|
||||||
transactions = get_transaction_entries(filename, statement_headers)
|
transactions = get_transaction_entries(file_url, statement_headers)
|
||||||
for entry in transactions:
|
for entry in transactions:
|
||||||
date = entry[statement_headers["Date"]].strip()
|
date = entry[statement_headers["Date"]].strip()
|
||||||
#print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"]))
|
#print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"]))
|
||||||
@ -398,20 +398,21 @@ def get_transaction_info(headers, header_index, row):
|
|||||||
transaction[header] = ""
|
transaction[header] = ""
|
||||||
return transaction
|
return transaction
|
||||||
|
|
||||||
def get_transaction_entries(filename, headers):
|
def get_transaction_entries(file_url, headers):
|
||||||
header_index = {}
|
header_index = {}
|
||||||
rows, transactions = [], []
|
rows, transactions = [], []
|
||||||
|
|
||||||
if (filename.lower().endswith("xlsx")):
|
if (file_url.lower().endswith("xlsx")):
|
||||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||||
rows = read_xlsx_file_from_attached_file(file_id=filename)
|
rows = read_xlsx_file_from_attached_file(file_url=file_url)
|
||||||
elif (filename.lower().endswith("csv")):
|
elif (file_url.lower().endswith("csv")):
|
||||||
from frappe.utils.csvutils import read_csv_content
|
from frappe.utils.csvutils import read_csv_content
|
||||||
_file = frappe.get_doc("File", {"file_name": filename})
|
_file = frappe.get_doc("File", {"file_url": file_url})
|
||||||
filepath = _file.get_full_path()
|
filepath = _file.get_full_path()
|
||||||
with open(filepath,'rb') as csvfile:
|
with open(filepath,'rb') as csvfile:
|
||||||
rows = read_csv_content(csvfile.read())
|
rows = read_csv_content(csvfile.read())
|
||||||
elif (filename.lower().endswith("xls")):
|
elif (file_url.lower().endswith("xls")):
|
||||||
|
filename = file_url.split("/")[-1]
|
||||||
rows = get_rows_from_xls_file(filename)
|
rows = get_rows_from_xls_file(filename)
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("Only .csv and .xlsx files are supported currently"))
|
frappe.throw(_("Only .csv and .xlsx files are supported currently"))
|
||||||
|
@ -91,15 +91,11 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
|
self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
|
||||||
self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
|
self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
|
||||||
|
|
||||||
def add_transactions():
|
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
||||||
if frappe.flags.test_bank_transactions_created:
|
|
||||||
return
|
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
try:
|
try:
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Bank",
|
"doctype": "Bank",
|
||||||
"bank_name":"Citi Bank",
|
"bank_name":bank_name,
|
||||||
}).insert()
|
}).insert()
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
@ -108,12 +104,19 @@ def add_transactions():
|
|||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Bank Account",
|
"doctype": "Bank Account",
|
||||||
"account_name":"Checking Account",
|
"account_name":"Checking Account",
|
||||||
"bank": "Citi Bank",
|
"bank": bank_name,
|
||||||
"account": "_Test Bank - _TC"
|
"account": account_name
|
||||||
}).insert()
|
}).insert()
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def add_transactions():
|
||||||
|
if frappe.flags.test_bank_transactions_created:
|
||||||
|
return
|
||||||
|
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
create_bank_account()
|
||||||
|
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc({
|
||||||
"doctype": "Bank Transaction",
|
"doctype": "Bank Transaction",
|
||||||
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
|
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
|
||||||
|
@ -38,8 +38,8 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2020-06-18 20:27:42.615842",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "ahmad@havenir.com",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Item Tax Template",
|
"name": "Item Tax Template",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
@ -638,20 +638,12 @@ $.extend(erpnext.journal_entry, {
|
|||||||
return { filters: filters };
|
return { filters: filters };
|
||||||
},
|
},
|
||||||
|
|
||||||
reverse_journal_entry: function(frm) {
|
reverse_journal_entry: function() {
|
||||||
var me = frm.doc;
|
frappe.model.open_mapped_doc({
|
||||||
for(var i=0; i<me.accounts.length; i++) {
|
method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
|
||||||
me.accounts[i].credit += me.accounts[i].debit;
|
frm: cur_frm
|
||||||
me.accounts[i].debit = me.accounts[i].credit - me.accounts[i].debit;
|
})
|
||||||
me.accounts[i].credit -= me.accounts[i].debit;
|
},
|
||||||
me.accounts[i].credit_in_account_currency = me.accounts[i].credit;
|
|
||||||
me.accounts[i].debit_in_account_currency = me.accounts[i].debit;
|
|
||||||
me.accounts[i].reference_type = "Journal Entry";
|
|
||||||
me.accounts[i].reference_name = me.name
|
|
||||||
}
|
|
||||||
frm.copy_doc();
|
|
||||||
cur_frm.reload_doc();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$.extend(erpnext.journal_entry, {
|
$.extend(erpnext.journal_entry, {
|
||||||
|
@ -1021,3 +1021,34 @@ def make_inter_company_journal_entry(name, voucher_type, company):
|
|||||||
journal_entry.posting_date = nowdate()
|
journal_entry.posting_date = nowdate()
|
||||||
journal_entry.inter_company_journal_entry_reference = name
|
journal_entry.inter_company_journal_entry_reference = name
|
||||||
return journal_entry.as_dict()
|
return journal_entry.as_dict()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_reverse_journal_entry(source_name, target_doc=None):
|
||||||
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
|
def update_accounts(source, target, source_parent):
|
||||||
|
target.reference_type = "Journal Entry"
|
||||||
|
target.reference_name = source_parent.name
|
||||||
|
|
||||||
|
doclist = get_mapped_doc("Journal Entry", source_name, {
|
||||||
|
"Journal Entry": {
|
||||||
|
"doctype": "Journal Entry",
|
||||||
|
"validation": {
|
||||||
|
"docstatus": ["=", 1]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Journal Entry Account": {
|
||||||
|
"doctype": "Journal Entry Account",
|
||||||
|
"field_map": {
|
||||||
|
"account_currency": "account_currency",
|
||||||
|
"exchange_rate": "exchange_rate",
|
||||||
|
"debit_in_account_currency": "credit_in_account_currency",
|
||||||
|
"debit": "credit",
|
||||||
|
"credit_in_account_currency": "debit_in_account_currency",
|
||||||
|
"credit": "debit",
|
||||||
|
},
|
||||||
|
"postprocess": update_accounts,
|
||||||
|
},
|
||||||
|
}, target_doc)
|
||||||
|
|
||||||
|
return doclist
|
@ -167,6 +167,49 @@ class TestJournalEntry(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertFalse(gle)
|
self.assertFalse(gle)
|
||||||
|
|
||||||
|
def test_reverse_journal_entry(self):
|
||||||
|
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
|
||||||
|
jv = make_journal_entry("_Test Bank USD - _TC",
|
||||||
|
"Sales - _TC", 100, exchange_rate=50, save=False)
|
||||||
|
|
||||||
|
jv.get("accounts")[1].credit_in_account_currency = 5000
|
||||||
|
jv.get("accounts")[1].exchange_rate = 1
|
||||||
|
jv.submit()
|
||||||
|
|
||||||
|
rjv = make_reverse_journal_entry(jv.name)
|
||||||
|
rjv.posting_date = nowdate()
|
||||||
|
rjv.submit()
|
||||||
|
|
||||||
|
|
||||||
|
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||||
|
debit_in_account_currency, credit_in_account_currency
|
||||||
|
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
|
||||||
|
order by account asc""", rjv.name, as_dict=1)
|
||||||
|
|
||||||
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
|
|
||||||
|
expected_values = {
|
||||||
|
"_Test Bank USD - _TC": {
|
||||||
|
"account_currency": "USD",
|
||||||
|
"debit": 0,
|
||||||
|
"debit_in_account_currency": 0,
|
||||||
|
"credit": 5000,
|
||||||
|
"credit_in_account_currency": 100,
|
||||||
|
},
|
||||||
|
"Sales - _TC": {
|
||||||
|
"account_currency": "INR",
|
||||||
|
"debit": 5000,
|
||||||
|
"debit_in_account_currency": 5000,
|
||||||
|
"credit": 0,
|
||||||
|
"credit_in_account_currency": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
|
||||||
|
for i, gle in enumerate(gl_entries):
|
||||||
|
self.assertEqual(expected_values[gle.account][field], gle[field])
|
||||||
|
|
||||||
def test_disallow_change_in_account_currency_for_a_party(self):
|
def test_disallow_change_in_account_currency_for_a_party(self):
|
||||||
# create jv in USD
|
# create jv in USD
|
||||||
jv = make_journal_entry("_Test Bank USD - _TC",
|
jv = make_journal_entry("_Test Bank USD - _TC",
|
||||||
|
@ -45,11 +45,11 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-credit-card",
|
"icon": "fa fa-credit-card",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-08-14 14:58:42.079115",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "sammish.thundiyil@gmail.com",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Mode of Payment",
|
"name": "Mode of Payment",
|
||||||
"owner": "harshada@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"create": 1,
|
"create": 1,
|
||||||
|
@ -12,9 +12,10 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("paid_from", function() {
|
frm.set_query("paid_from", function() {
|
||||||
|
frm.events.validate_company(frm);
|
||||||
|
|
||||||
var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
|
var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
|
||||||
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
|
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"account_type": ["in", account_types],
|
"account_type": ["in", account_types],
|
||||||
@ -23,13 +24,16 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("party_type", function() {
|
frm.set_query("party_type", function() {
|
||||||
|
frm.events.validate_company(frm);
|
||||||
return{
|
return{
|
||||||
filters: {
|
filters: {
|
||||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("party_bank_account", function() {
|
frm.set_query("party_bank_account", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
@ -39,6 +43,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("bank_account", function() {
|
frm.set_query("bank_account", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
@ -47,6 +52,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("contact_person", function() {
|
frm.set_query("contact_person", function() {
|
||||||
if (frm.doc.party) {
|
if (frm.doc.party) {
|
||||||
return {
|
return {
|
||||||
@ -58,10 +64,12 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("paid_to", function() {
|
frm.set_query("paid_to", function() {
|
||||||
|
frm.events.validate_company(frm);
|
||||||
|
|
||||||
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
|
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
|
||||||
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
|
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"account_type": ["in", account_types],
|
"account_type": ["in", account_types],
|
||||||
@ -150,6 +158,12 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.events.show_general_ledger(frm);
|
frm.events.show_general_ledger(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
validate_company: (frm) => {
|
||||||
|
if (!frm.doc.company){
|
||||||
|
frappe.throw({message:__("Please select a Company first."), title: __("Mandatory")});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
company: function(frm) {
|
company: function(frm) {
|
||||||
frm.events.hide_unhide_fields(frm);
|
frm.events.hide_unhide_fields(frm);
|
||||||
frm.events.set_dynamic_labels(frm);
|
frm.events.set_dynamic_labels(frm);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2016-06-01 14:38:51.012597",
|
"creation": "2016-06-01 14:38:51.012597",
|
||||||
@ -63,6 +64,7 @@
|
|||||||
"cost_center",
|
"cost_center",
|
||||||
"section_break_12",
|
"section_break_12",
|
||||||
"status",
|
"status",
|
||||||
|
"custom_remarks",
|
||||||
"remarks",
|
"remarks",
|
||||||
"column_break_16",
|
"column_break_16",
|
||||||
"letter_head",
|
"letter_head",
|
||||||
@ -462,7 +464,8 @@
|
|||||||
"fieldname": "remarks",
|
"fieldname": "remarks",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Remarks",
|
"label": "Remarks",
|
||||||
"no_copy": 1
|
"no_copy": 1,
|
||||||
|
"read_only_depends_on": "eval:doc.custom_remarks == 0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_16",
|
"fieldname": "column_break_16",
|
||||||
@ -573,10 +576,18 @@
|
|||||||
"label": "Status",
|
"label": "Status",
|
||||||
"options": "\nDraft\nSubmitted\nCancelled",
|
"options": "\nDraft\nSubmitted\nCancelled",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "custom_remarks",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Custom Remarks"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-12-08 13:02:30.016610",
|
"links": [],
|
||||||
|
"modified": "2020-09-02 13:39:43.383705",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
@ -453,7 +453,7 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
||||||
|
|
||||||
def set_remarks(self):
|
def set_remarks(self):
|
||||||
if self.remarks: return
|
if self.custom_remarks: return
|
||||||
|
|
||||||
if self.payment_type=="Internal Transfer":
|
if self.payment_type=="Internal Transfer":
|
||||||
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
||||||
@ -1172,30 +1172,23 @@ def make_payment_order(source_name, target_doc=None):
|
|||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.payment_order_type = "Payment Entry"
|
target.payment_order_type = "Payment Entry"
|
||||||
|
target.append('references', dict(
|
||||||
|
reference_doctype="Payment Entry",
|
||||||
|
reference_name=source.name,
|
||||||
|
bank_account=source.party_bank_account,
|
||||||
|
amount=source.paid_amount,
|
||||||
|
account=source.paid_to,
|
||||||
|
supplier=source.party,
|
||||||
|
mode_of_payment=source.mode_of_payment,
|
||||||
|
))
|
||||||
|
|
||||||
def update_item(source_doc, target_doc, source_parent):
|
doclist = get_mapped_doc("Payment Entry", source_name, {
|
||||||
target_doc.bank_account = source_parent.party_bank_account
|
|
||||||
target_doc.amount = source_doc.allocated_amount
|
|
||||||
target_doc.account = source_parent.paid_to
|
|
||||||
target_doc.payment_entry = source_parent.name
|
|
||||||
target_doc.supplier = source_parent.party
|
|
||||||
target_doc.mode_of_payment = source_parent.mode_of_payment
|
|
||||||
|
|
||||||
|
|
||||||
doclist = get_mapped_doc("Payment Entry", source_name, {
|
|
||||||
"Payment Entry": {
|
"Payment Entry": {
|
||||||
"doctype": "Payment Order",
|
"doctype": "Payment Order",
|
||||||
"validation": {
|
|
||||||
"docstatus": ["=", 1]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Payment Entry Reference": {
|
|
||||||
"doctype": "Payment Order Reference",
|
|
||||||
"validation": {
|
"validation": {
|
||||||
"docstatus": ["=", 1]
|
"docstatus": ["=", 1]
|
||||||
},
|
},
|
||||||
"postprocess": update_item
|
}
|
||||||
},
|
|
||||||
|
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
|
@ -21,10 +21,15 @@ class PaymentOrder(Document):
|
|||||||
if cancel:
|
if cancel:
|
||||||
status = 'Initiated'
|
status = 'Initiated'
|
||||||
|
|
||||||
ref_field = "status" if self.payment_order_type == "Payment Request" else "payment_order_status"
|
if self.payment_order_type == "Payment Request":
|
||||||
|
ref_field = "status"
|
||||||
|
ref_doc_field = frappe.scrub(self.payment_order_type)
|
||||||
|
else:
|
||||||
|
ref_field = "payment_order_status"
|
||||||
|
ref_doc_field = "reference_name"
|
||||||
|
|
||||||
for d in self.references:
|
for d in self.references:
|
||||||
frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status)
|
frappe.db.set_value(self.payment_order_type, d.get(ref_doc_field), ref_field, status)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
|
@ -5,6 +5,45 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
from frappe.utils import getdate
|
||||||
|
from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account
|
||||||
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, make_payment_order
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
|
|
||||||
class TestPaymentOrder(unittest.TestCase):
|
class TestPaymentOrder(unittest.TestCase):
|
||||||
pass
|
def setUp(self):
|
||||||
|
create_bank_account()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for bt in frappe.get_all("Payment Order"):
|
||||||
|
doc = frappe.get_doc("Payment Order", bt.name)
|
||||||
|
doc.cancel()
|
||||||
|
doc.delete()
|
||||||
|
|
||||||
|
def test_payment_order_creation_against_payment_entry(self):
|
||||||
|
purchase_invoice = make_purchase_invoice()
|
||||||
|
payment_entry = get_payment_entry("Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC")
|
||||||
|
payment_entry.reference_no = "_Test_Payment_Order"
|
||||||
|
payment_entry.reference_date = getdate()
|
||||||
|
payment_entry.party_bank_account = "Checking Account - Citi Bank"
|
||||||
|
payment_entry.insert()
|
||||||
|
payment_entry.submit()
|
||||||
|
|
||||||
|
doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry")
|
||||||
|
reference_doc = doc.get("references")[0]
|
||||||
|
self.assertEquals(reference_doc.reference_name, payment_entry.name)
|
||||||
|
self.assertEquals(reference_doc.reference_doctype, "Payment Entry")
|
||||||
|
self.assertEquals(reference_doc.supplier, "_Test Supplier")
|
||||||
|
self.assertEquals(reference_doc.amount, 250)
|
||||||
|
|
||||||
|
def create_payment_order_against_payment_entry(ref_doc, order_type):
|
||||||
|
payment_order = frappe.get_doc(dict(
|
||||||
|
doctype="Payment Order",
|
||||||
|
company="_Test Company",
|
||||||
|
payment_order_type=order_type,
|
||||||
|
company_bank_account="Checking Account - Citi Bank"
|
||||||
|
))
|
||||||
|
doc = make_payment_order(ref_doc.name, payment_order)
|
||||||
|
doc.save()
|
||||||
|
doc.submit()
|
||||||
|
return doc
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2018-07-20 16:38:06.630813",
|
"creation": "2018-07-20 16:38:06.630813",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@ -10,7 +11,6 @@
|
|||||||
"column_break_4",
|
"column_break_4",
|
||||||
"supplier",
|
"supplier",
|
||||||
"payment_request",
|
"payment_request",
|
||||||
"payment_entry",
|
|
||||||
"mode_of_payment",
|
"mode_of_payment",
|
||||||
"bank_account_details",
|
"bank_account_details",
|
||||||
"bank_account",
|
"bank_account",
|
||||||
@ -103,17 +103,12 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "payment_entry",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Payment Entry",
|
|
||||||
"options": "Payment Entry",
|
|
||||||
"read_only": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-05-08 13:56:25.724557",
|
"links": [],
|
||||||
|
"modified": "2020-09-04 08:29:51.014390",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Order Reference",
|
"name": "Payment Order Reference",
|
||||||
|
@ -291,11 +291,11 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-08-21 16:15:49.089450",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Period Closing Voucher",
|
"name": "Period Closing Voucher",
|
||||||
"owner": "jai@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
|
@ -55,14 +55,48 @@ frappe.ui.form.on('POS Closing Entry', {
|
|||||||
},
|
},
|
||||||
callback: (r) => {
|
callback: (r) => {
|
||||||
let pos_docs = r.message;
|
let pos_docs = r.message;
|
||||||
set_form_data(pos_docs, frm)
|
set_form_data(pos_docs, frm);
|
||||||
refresh_fields(frm)
|
refresh_fields(frm);
|
||||||
set_html_data(frm)
|
set_html_data(frm);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cur_frm.cscript.before_pos_transactions_remove = function(doc, cdt, cdn) {
|
||||||
|
const removed_row = locals[cdt][cdn];
|
||||||
|
|
||||||
|
if (!removed_row.pos_invoice) return;
|
||||||
|
|
||||||
|
frappe.db.get_doc("POS Invoice", removed_row.pos_invoice).then(doc => {
|
||||||
|
cur_frm.doc.grand_total -= flt(doc.grand_total);
|
||||||
|
cur_frm.doc.net_total -= flt(doc.net_total);
|
||||||
|
cur_frm.doc.total_quantity -= flt(doc.total_qty);
|
||||||
|
refresh_payments(doc, cur_frm, 1);
|
||||||
|
refresh_taxes(doc, cur_frm, 1);
|
||||||
|
refresh_fields(cur_frm);
|
||||||
|
set_html_data(cur_frm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.ui.form.on('POS Invoice Reference', {
|
||||||
|
pos_invoice(frm, cdt, cdn) {
|
||||||
|
const added_row = locals[cdt][cdn];
|
||||||
|
|
||||||
|
if (!added_row.pos_invoice) return;
|
||||||
|
|
||||||
|
frappe.db.get_doc("POS Invoice", added_row.pos_invoice).then(doc => {
|
||||||
|
frm.doc.grand_total += flt(doc.grand_total);
|
||||||
|
frm.doc.net_total += flt(doc.net_total);
|
||||||
|
frm.doc.total_quantity += flt(doc.total_qty);
|
||||||
|
refresh_payments(doc, frm);
|
||||||
|
refresh_taxes(doc, frm);
|
||||||
|
refresh_fields(frm);
|
||||||
|
set_html_data(frm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
frappe.ui.form.on('POS Closing Entry Detail', {
|
frappe.ui.form.on('POS Closing Entry Detail', {
|
||||||
closing_amount: (frm, cdt, cdn) => {
|
closing_amount: (frm, cdt, cdn) => {
|
||||||
const row = locals[cdt][cdn];
|
const row = locals[cdt][cdn];
|
||||||
@ -76,8 +110,8 @@ function set_form_data(data, frm) {
|
|||||||
frm.doc.grand_total += flt(d.grand_total);
|
frm.doc.grand_total += flt(d.grand_total);
|
||||||
frm.doc.net_total += flt(d.net_total);
|
frm.doc.net_total += flt(d.net_total);
|
||||||
frm.doc.total_quantity += flt(d.total_qty);
|
frm.doc.total_quantity += flt(d.total_qty);
|
||||||
add_to_payments(d, frm);
|
refresh_payments(d, frm);
|
||||||
add_to_taxes(d, frm);
|
refresh_taxes(d, frm);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,11 +124,12 @@ function add_to_pos_transaction(d, frm) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_to_payments(d, frm) {
|
function refresh_payments(d, frm, remove) {
|
||||||
d.payments.forEach(p => {
|
d.payments.forEach(p => {
|
||||||
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
|
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
|
||||||
if (payment) {
|
if (payment) {
|
||||||
payment.expected_amount += flt(p.amount);
|
if (!remove) payment.expected_amount += flt(p.amount);
|
||||||
|
else payment.expected_amount -= flt(p.amount);
|
||||||
} else {
|
} else {
|
||||||
frm.add_child("payment_reconciliation", {
|
frm.add_child("payment_reconciliation", {
|
||||||
mode_of_payment: p.mode_of_payment,
|
mode_of_payment: p.mode_of_payment,
|
||||||
@ -105,11 +140,12 @@ function add_to_payments(d, frm) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_to_taxes(d, frm) {
|
function refresh_taxes(d, frm, remove) {
|
||||||
d.taxes.forEach(t => {
|
d.taxes.forEach(t => {
|
||||||
const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
|
const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
|
||||||
if (tax) {
|
if (tax) {
|
||||||
tax.amount += flt(t.tax_amount);
|
if (!remove) tax.amount += flt(t.tax_amount);
|
||||||
|
else tax.amount -= flt(t.tax_amount);
|
||||||
} else {
|
} else {
|
||||||
frm.add_child("taxes", {
|
frm.add_child("taxes", {
|
||||||
account_head: t.account_head,
|
account_head: t.account_head,
|
||||||
|
@ -279,7 +279,8 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Return (Credit Note)",
|
"label": "Is Return (Credit Note)",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"set_only_once": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break1",
|
"fieldname": "column_break1",
|
||||||
@ -1578,9 +1579,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-29 15:08:39.337385",
|
"modified": "2020-09-07 12:43:09.138720",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Invoice",
|
"name": "POS Invoice",
|
||||||
|
@ -182,8 +182,9 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
def test_pos_returns_with_repayment(self):
|
def test_pos_returns_with_repayment(self):
|
||||||
pos = create_pos_invoice(qty = 10, do_not_save=True)
|
pos = create_pos_invoice(qty = 10, do_not_save=True)
|
||||||
|
|
||||||
|
pos.set('payments', [])
|
||||||
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
|
||||||
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500})
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500, 'default': 1})
|
||||||
pos.insert()
|
pos.insert()
|
||||||
pos.submit()
|
pos.submit()
|
||||||
|
|
||||||
@ -200,8 +201,9 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
|
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
|
||||||
cost_center = "Main - _TC", do_not_save=True)
|
cost_center = "Main - _TC", do_not_save=True)
|
||||||
|
|
||||||
|
pos.set('payments', [])
|
||||||
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 50})
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 50})
|
||||||
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60})
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60, 'default': 1})
|
||||||
|
|
||||||
pos.insert()
|
pos.insert()
|
||||||
pos.submit()
|
pos.submit()
|
||||||
|
@ -24,11 +24,20 @@ class POSInvoiceMergeLog(Document):
|
|||||||
|
|
||||||
def validate_pos_invoice_status(self):
|
def validate_pos_invoice_status(self):
|
||||||
for d in self.pos_invoices:
|
for d in self.pos_invoices:
|
||||||
status, docstatus = frappe.db.get_value('POS Invoice', d.pos_invoice, ['status', 'docstatus'])
|
status, docstatus, is_return, return_against = frappe.db.get_value(
|
||||||
|
'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
|
||||||
|
|
||||||
if docstatus != 1:
|
if docstatus != 1:
|
||||||
frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, d.pos_invoice))
|
frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, d.pos_invoice))
|
||||||
if status in ['Consolidated']:
|
if status == "Consolidated":
|
||||||
frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, d.pos_invoice, status))
|
frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, d.pos_invoice, status))
|
||||||
|
if is_return and return_against not in [d.pos_invoice for d in self.pos_invoices] and status != "Consolidated":
|
||||||
|
# if return entry is not getting merged in the current pos closing and if it is not consolidated
|
||||||
|
frappe.throw(
|
||||||
|
_("Row #{}: Return Invoice {} cannot be made against unconsolidated invoice. \
|
||||||
|
You can add original invoice {} manually to proceed.")
|
||||||
|
.format(d.idx, frappe.bold(d.pos_invoice), frappe.bold(return_against))
|
||||||
|
)
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
||||||
@ -36,12 +45,12 @@ class POSInvoiceMergeLog(Document):
|
|||||||
returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
|
returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
|
||||||
sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
|
sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
|
||||||
|
|
||||||
sales_invoice = self.process_merging_into_sales_invoice(sales)
|
sales_invoice, credit_note = "", ""
|
||||||
|
if sales:
|
||||||
|
sales_invoice = self.process_merging_into_sales_invoice(sales)
|
||||||
|
|
||||||
if len(returns):
|
if returns:
|
||||||
credit_note = self.process_merging_into_credit_note(returns)
|
credit_note = self.process_merging_into_credit_note(returns)
|
||||||
else:
|
|
||||||
credit_note = ""
|
|
||||||
|
|
||||||
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
||||||
|
|
||||||
|
@ -5,13 +5,14 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint, get_link_to_form
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.controllers.status_updater import StatusUpdater
|
from erpnext.controllers.status_updater import StatusUpdater
|
||||||
|
|
||||||
class POSOpeningEntry(StatusUpdater):
|
class POSOpeningEntry(StatusUpdater):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_pos_profile_and_cashier()
|
self.validate_pos_profile_and_cashier()
|
||||||
|
self.validate_payment_method_account()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
def validate_pos_profile_and_cashier(self):
|
def validate_pos_profile_and_cashier(self):
|
||||||
@ -20,6 +21,14 @@ class POSOpeningEntry(StatusUpdater):
|
|||||||
|
|
||||||
if not cint(frappe.db.get_value("User", self.user, "enabled")):
|
if not cint(frappe.db.get_value("User", self.user, "enabled")):
|
||||||
frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user)))
|
frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user)))
|
||||||
|
|
||||||
|
def validate_payment_method_account(self):
|
||||||
|
for d in self.balance_details:
|
||||||
|
account = frappe.db.get_value("Mode of Payment Account",
|
||||||
|
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
|
||||||
|
if not account:
|
||||||
|
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
||||||
|
.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.set_status(update=True)
|
self.set_status(update=True)
|
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from frappe.utils import cint, now
|
from frappe.utils import cint, now, get_link_to_form
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ class POSProfile(Document):
|
|||||||
self.validate_default_profile()
|
self.validate_default_profile()
|
||||||
self.validate_all_link_fields()
|
self.validate_all_link_fields()
|
||||||
self.validate_duplicate_groups()
|
self.validate_duplicate_groups()
|
||||||
self.check_default_payment()
|
self.validate_payment_methods()
|
||||||
|
|
||||||
def validate_default_profile(self):
|
def validate_default_profile(self):
|
||||||
for row in self.applicable_for_users:
|
for row in self.applicable_for_users:
|
||||||
@ -52,14 +52,23 @@ class POSProfile(Document):
|
|||||||
if len(customer_groups) != len(set(customer_groups)):
|
if len(customer_groups) != len(set(customer_groups)):
|
||||||
frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group")
|
frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group")
|
||||||
|
|
||||||
def check_default_payment(self):
|
def validate_payment_methods(self):
|
||||||
if self.payments:
|
if not self.payments:
|
||||||
default_mode_of_payment = [d.default for d in self.payments if d.default]
|
frappe.throw(_("Payment methods are mandatory. Please add at least one payment method."))
|
||||||
if not default_mode_of_payment:
|
|
||||||
frappe.throw(_("Set default mode of payment"))
|
|
||||||
|
|
||||||
if len(default_mode_of_payment) > 1:
|
default_mode_of_payment = [d.default for d in self.payments if d.default]
|
||||||
frappe.throw(_("Multiple default mode of payment is not allowed"))
|
if not default_mode_of_payment:
|
||||||
|
frappe.throw(_("Please select a default mode of payment"))
|
||||||
|
|
||||||
|
if len(default_mode_of_payment) > 1:
|
||||||
|
frappe.throw(_("You can only select one mode of payment as default"))
|
||||||
|
|
||||||
|
for d in self.payments:
|
||||||
|
account = frappe.db.get_value("Mode of Payment Account",
|
||||||
|
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
|
||||||
|
if not account:
|
||||||
|
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
||||||
|
.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
|
@ -7,10 +7,10 @@ frappe.ui.form.on('POS Settings', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
get_invoice_fields: function(frm) {
|
get_invoice_fields: function(frm) {
|
||||||
frappe.model.with_doctype("Sales Invoice", () => {
|
frappe.model.with_doctype("POS Invoice", () => {
|
||||||
var fields = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
|
var fields = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
|
||||||
if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
|
if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
|
||||||
d.fieldtype === 'Table') {
|
['Table', 'Button'].includes(d.fieldtype)) {
|
||||||
return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
|
return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -25,7 +25,7 @@ frappe.ui.form.on('POS Settings', {
|
|||||||
frappe.ui.form.on("POS Field", {
|
frappe.ui.form.on("POS Field", {
|
||||||
fieldname: function(frm, doctype, name) {
|
fieldname: function(frm, doctype, name) {
|
||||||
var doc = frappe.get_doc(doctype, name);
|
var doc = frappe.get_doc(doctype, name);
|
||||||
var df = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
|
var df = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
|
||||||
return doc.fieldname == d.fieldname ? d : null;
|
return doc.fieldname == d.fieldname ? d : null;
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
|
@ -10,13 +10,15 @@ frappe.ui.form.on('Process Deferred Accounting', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
if (frm.doc.company) {
|
type: function(frm) {
|
||||||
|
if (frm.doc.company && frm.doc.type) {
|
||||||
frm.set_query("account", function() {
|
frm.set_query("account", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'company': frm.doc.company,
|
'company': frm.doc.company,
|
||||||
'root_type': 'Liability',
|
'root_type': frm.doc.type === 'Income' ? 'Liability' : 'Asset',
|
||||||
'is_group': 0
|
'is_group': 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.type",
|
||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Account",
|
"label": "Account",
|
||||||
@ -73,9 +74,10 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-02-06 18:18:09.852844",
|
"modified": "2020-09-03 18:07:02.463754",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Process Deferred Accounting",
|
"name": "Process Deferred Accounting",
|
||||||
|
@ -25,6 +25,12 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
this.frm.set_df_property("credit_to", "print_hide", 0);
|
this.frm.set_df_property("credit_to", "print_hide", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trigger supplier event on load if supplier is available
|
||||||
|
// The reason for this is PI can be created from PR or PO and supplier is pre populated
|
||||||
|
if (this.frm.doc.supplier && this.frm.doc.__islocal) {
|
||||||
|
this.frm.trigger('supplier');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(doc) {
|
refresh: function(doc) {
|
||||||
@ -135,6 +141,8 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.frm.set_df_property("tax_withholding_category", "hidden", doc.apply_tds ? 0 : 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
unblock_invoice: function() {
|
unblock_invoice: function() {
|
||||||
|
@ -132,6 +132,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.due_date:
|
if not self.due_date:
|
||||||
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company, self.bill_date)
|
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company, self.bill_date)
|
||||||
|
|
||||||
|
tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
||||||
|
if tds_category and not for_validate:
|
||||||
|
self.apply_tds = 1
|
||||||
|
self.tax_withholding_category = tds_category
|
||||||
|
|
||||||
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
||||||
|
|
||||||
def check_conversion_rate(self):
|
def check_conversion_rate(self):
|
||||||
@ -400,8 +405,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||||
|
|
||||||
def make_gl_entries(self, gl_entries=None):
|
def make_gl_entries(self, gl_entries=None):
|
||||||
if not self.grand_total:
|
|
||||||
return
|
|
||||||
if not gl_entries:
|
if not gl_entries:
|
||||||
gl_entries = self.get_gl_entries()
|
gl_entries = self.get_gl_entries()
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-12 14:53:47.679439",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Taxes and Charges",
|
"name": "Purchase Taxes and Charges",
|
||||||
|
@ -78,7 +78,7 @@
|
|||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Taxes and Charges Template",
|
"name": "Purchase Taxes and Charges Template",
|
||||||
"owner": "wasim@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
|
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate, get_link_to_form
|
||||||
from frappe import _, msgprint, throw
|
from frappe import _, msgprint, throw
|
||||||
from erpnext.accounts.party import get_party_account, get_due_date
|
from erpnext.accounts.party import get_party_account, get_due_date
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
@ -1372,7 +1372,7 @@ def get_bank_cash_account(mode_of_payment, company):
|
|||||||
{"parent": mode_of_payment, "company": company}, "default_account")
|
{"parent": mode_of_payment, "company": company}, "default_account")
|
||||||
if not account:
|
if not account:
|
||||||
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
||||||
.format(mode_of_payment))
|
.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
|
||||||
return {
|
return {
|
||||||
"account": account
|
"account": account
|
||||||
}
|
}
|
||||||
@ -1612,18 +1612,16 @@ def update_multi_mode_option(doc, pos_profile):
|
|||||||
payment.type = payment_mode.type
|
payment.type = payment_mode.type
|
||||||
|
|
||||||
doc.set('payments', [])
|
doc.set('payments', [])
|
||||||
if not pos_profile or not pos_profile.get('payments'):
|
|
||||||
for payment_mode in get_all_mode_of_payments(doc):
|
|
||||||
append_payment(payment_mode)
|
|
||||||
return
|
|
||||||
|
|
||||||
for pos_payment_method in pos_profile.get('payments'):
|
for pos_payment_method in pos_profile.get('payments'):
|
||||||
pos_payment_method = pos_payment_method.as_dict()
|
pos_payment_method = pos_payment_method.as_dict()
|
||||||
|
|
||||||
payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
|
payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
|
||||||
if payment_mode:
|
if not payment_mode:
|
||||||
payment_mode[0].default = pos_payment_method.default
|
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
||||||
append_payment(payment_mode[0])
|
.format(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment)), title=_("Missing Account"))
|
||||||
|
|
||||||
|
payment_mode[0].default = pos_payment_method.default
|
||||||
|
append_payment(payment_mode[0])
|
||||||
|
|
||||||
def get_all_mode_of_payments(doc):
|
def get_all_mode_of_payments(doc):
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
|
@ -1,134 +1,66 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"allow_rename": 1,
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "field:title",
|
"autoname": "field:title",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2018-11-22 23:38:39.668804",
|
"creation": "2018-11-22 23:38:39.668804",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"title"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "title",
|
"fieldname": "title",
|
||||||
"fieldtype": "Data",
|
"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",
|
"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": 1
|
"unique": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2020-08-30 19:41:25.783852",
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2020-01-15 17:14:28.951793",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Tax Category",
|
"name": "Tax Category",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts User",
|
"role": "Accounts User",
|
||||||
"set_user_permissions": 0,
|
"share": 1
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
}
|
||||||
"track_views": 0
|
|
||||||
}
|
|
@ -45,8 +45,8 @@ def validate_accounting_period(gl_map):
|
|||||||
}, as_dict=1)
|
}, as_dict=1)
|
||||||
|
|
||||||
if accounting_periods:
|
if accounting_periods:
|
||||||
frappe.throw(_("You can't create accounting entries in the closed accounting period {0}")
|
frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
|
||||||
.format(accounting_periods[0].name), ClosedAccountingPeriod)
|
.format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
|
||||||
|
|
||||||
def process_gl_map(gl_map, merge_entries=True):
|
def process_gl_map(gl_map, merge_entries=True):
|
||||||
if merge_entries:
|
if merge_entries:
|
||||||
@ -301,8 +301,9 @@ def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
|||||||
})
|
})
|
||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
|
validate_accounting_period(gl_entries)
|
||||||
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
||||||
|
set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
entry['name'] = None
|
entry['name'] = None
|
||||||
@ -319,7 +320,6 @@ def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
|||||||
|
|
||||||
entry['remarks'] = "On cancellation of " + entry['voucher_no']
|
entry['remarks'] = "On cancellation of " + entry['voucher_no']
|
||||||
entry['is_cancelled'] = 1
|
entry['is_cancelled'] = 1
|
||||||
entry['posting_date'] = today()
|
|
||||||
|
|
||||||
if entry['debit'] or entry['credit']:
|
if entry['debit'] or entry['credit']:
|
||||||
make_entry(entry, adv_adj, "Yes")
|
make_entry(entry, adv_adj, "Yes")
|
||||||
@ -342,7 +342,7 @@ def set_as_cancel(voucher_type, voucher_no):
|
|||||||
"""
|
"""
|
||||||
Set is_cancelled=1 in all original gl entries for the voucher
|
Set is_cancelled=1 in all original gl entries for the voucher
|
||||||
"""
|
"""
|
||||||
frappe.db.sql("""update `tabGL Entry` set is_cancelled = 1,
|
frappe.db.sql("""UPDATE `tabGL Entry` SET is_cancelled = 1,
|
||||||
modified=%s, modified_by=%s
|
modified=%s, modified_by=%s
|
||||||
where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
|
where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
|
||||||
(now(), frappe.session.user, voucher_type, voucher_no))
|
(now(), frappe.session.user, voucher_type, voucher_no))
|
||||||
|
@ -72,7 +72,7 @@ erpnext.accounts.bankReconciliation = class BankReconciliation {
|
|||||||
check_plaid_status() {
|
check_plaid_status() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
|
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
|
||||||
if (r && r.enabled == "1") {
|
if (r && r.enabled === "1") {
|
||||||
me.plaid_status = "active"
|
me.plaid_status = "active"
|
||||||
} else {
|
} else {
|
||||||
me.plaid_status = "inactive"
|
me.plaid_status = "inactive"
|
||||||
@ -139,7 +139,7 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make() {
|
make() {
|
||||||
const me = this;
|
const me = this;
|
||||||
new frappe.ui.FileUploader({
|
new frappe.ui.FileUploader({
|
||||||
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,
|
||||||
@ -214,31 +214,35 @@ erpnext.accounts.bankTransactionSync = class bankTransactionSync {
|
|||||||
|
|
||||||
init_config() {
|
init_config() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration')
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration')
|
||||||
.then(result => {
|
.then(result => {
|
||||||
me.plaid_env = result.plaid_env;
|
me.plaid_env = result.plaid_env;
|
||||||
me.plaid_public_key = result.plaid_public_key;
|
me.client_name = result.client_name;
|
||||||
me.client_name = result.client_name;
|
me.link_token = result.link_token;
|
||||||
me.sync_transactions()
|
me.sync_transactions();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sync_transactions() {
|
sync_transactions() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (v) => {
|
frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => {
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
|
||||||
bank: v['bank'],
|
bank: r.bank,
|
||||||
bank_account: me.parent.bank_account,
|
bank_account: me.parent.bank_account,
|
||||||
freeze: true
|
freeze: true
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
let result_title = (result.length > 0) ? __("{0} bank transaction(s) created", [result.length]) : __("This bank account is already synchronized")
|
let result_title = (result && result.length > 0)
|
||||||
|
? __("{0} bank transaction(s) created", [result.length])
|
||||||
|
: __("This bank account is already synchronized");
|
||||||
|
|
||||||
let result_msg = `
|
let result_msg = `
|
||||||
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
|
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
|
||||||
<h5 class="text-muted">${result_title}</h5>
|
<h5 class="text-muted">${result_title}</h5>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
this.parent.$main_section.append(result_msg)
|
this.parent.$main_section.append(result_msg)
|
||||||
frappe.show_alert({message:__("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator:'green'});
|
frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' });
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -384,7 +388,7 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
|
|||||||
})
|
})
|
||||||
|
|
||||||
frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
|
frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
|
||||||
{bank_transaction: data, freeze:true, freeze_message:__("Finding linked payments")}
|
{ bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") }
|
||||||
).then((result) => {
|
).then((result) => {
|
||||||
me.make_dialog(result)
|
me.make_dialog(result)
|
||||||
})
|
})
|
||||||
|
@ -34,7 +34,7 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
filters: {
|
filters: {
|
||||||
'company': company
|
'company': company
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -617,9 +617,19 @@ class ReceivablePayableReport(object):
|
|||||||
elif party_type_field=="supplier":
|
elif party_type_field=="supplier":
|
||||||
self.add_supplier_filters(conditions, values)
|
self.add_supplier_filters(conditions, values)
|
||||||
|
|
||||||
|
if self.filters.cost_center:
|
||||||
|
self.get_cost_center_conditions(conditions)
|
||||||
|
|
||||||
self.add_accounting_dimensions_filters(conditions, values)
|
self.add_accounting_dimensions_filters(conditions, values)
|
||||||
return " and ".join(conditions), values
|
return " and ".join(conditions), values
|
||||||
|
|
||||||
|
def get_cost_center_conditions(self, conditions):
|
||||||
|
lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"])
|
||||||
|
cost_center_list = [center.name for center in frappe.get_list("Cost Center", filters = {'lft': (">=", lft), 'rgt': ("<=", rgt)})]
|
||||||
|
|
||||||
|
cost_center_string = '", "'.join(cost_center_list)
|
||||||
|
conditions.append('cost_center in ("{0}")'.format(cost_center_string))
|
||||||
|
|
||||||
def get_order_by_condition(self):
|
def get_order_by_condition(self):
|
||||||
if self.filters.get('group_by_party'):
|
if self.filters.get('group_by_party'):
|
||||||
return "order by party, posting_date"
|
return "order by party, posting_date"
|
||||||
|
@ -71,7 +71,22 @@ frappe.query_reports["Budget Variance Report"] = {
|
|||||||
fieldtype: "Check",
|
fieldtype: "Check",
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
"formatter": function (value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
|
||||||
|
if (column.fieldname.includes('variance')) {
|
||||||
|
|
||||||
|
if (data[column.fieldname] < 0) {
|
||||||
|
value = "<span style='color:red'>" + value + "</span>";
|
||||||
|
}
|
||||||
|
else if (data[column.fieldname] > 0) {
|
||||||
|
value = "<span style='color:green'>" + value + "</span>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
erpnext.dimension_filters.forEach((dimension) => {
|
erpnext.dimension_filters.forEach((dimension) => {
|
||||||
|
@ -256,7 +256,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
|||||||
"""accumulate children's values in parent accounts"""
|
"""accumulate children's values in parent accounts"""
|
||||||
for d in reversed(accounts):
|
for d in reversed(accounts):
|
||||||
if d.parent_account:
|
if d.parent_account:
|
||||||
account = d.parent_account.split('-')[0].strip()
|
account = d.parent_account.split(' - ')[0].strip()
|
||||||
if not accounts_by_name.get(account):
|
if not accounts_by_name.get(account):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ class PartyLedgerSummaryReport(object):
|
|||||||
from `tabGL Entry` gle
|
from `tabGL Entry` gle
|
||||||
{join}
|
{join}
|
||||||
where
|
where
|
||||||
gle.docstatus < 2 and gle.party_type=%(party_type)s and ifnull(gle.party, '') != ''
|
gle.docstatus < 2 and gle.is_cancelled = 0 and gle.party_type=%(party_type)s and ifnull(gle.party, '') != ''
|
||||||
and gle.posting_date <= %(to_date)s {conditions}
|
and gle.posting_date <= %(to_date)s {conditions}
|
||||||
order by gle.posting_date
|
order by gle.posting_date
|
||||||
""".format(join=join, join_field=join_field, conditions=conditions), self.filters, as_dict=True)
|
""".format(join=join, join_field=join_field, conditions=conditions), self.filters, as_dict=True)
|
||||||
@ -248,7 +248,7 @@ class PartyLedgerSummaryReport(object):
|
|||||||
from
|
from
|
||||||
`tabGL Entry`
|
`tabGL Entry`
|
||||||
where
|
where
|
||||||
docstatus < 2
|
docstatus < 2 and is_cancelled = 0
|
||||||
and (voucher_type, voucher_no) in (
|
and (voucher_type, voucher_no) in (
|
||||||
select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc
|
select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc
|
||||||
where acc.name = gle.account and acc.account_type = '{income_or_expense}'
|
where acc.name = gle.account and acc.account_type = '{income_or_expense}'
|
||||||
|
@ -43,8 +43,11 @@ def execute(filters=None):
|
|||||||
|
|
||||||
|
|
||||||
def validate_filters(filters, account_details):
|
def validate_filters(filters, account_details):
|
||||||
if not filters.get('company'):
|
if not filters.get("company"):
|
||||||
frappe.throw(_('{0} is mandatory').format(_('Company')))
|
frappe.throw(_("{0} is mandatory").format(_("Company")))
|
||||||
|
|
||||||
|
if not filters.get("from_date") and not filters.get("to_date"):
|
||||||
|
frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
|
||||||
|
|
||||||
if filters.get("account") and not account_details.get(filters.account):
|
if filters.get("account") and not account_details.get(filters.account):
|
||||||
frappe.throw(_("Account {0} does not exists").format(filters.account))
|
frappe.throw(_("Account {0} does not exists").format(filters.account))
|
||||||
@ -141,7 +144,7 @@ def get_gl_entries(filters, accounting_dimensions):
|
|||||||
if filters and filters.get('cost_center'):
|
if filters and filters.get('cost_center'):
|
||||||
select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
|
select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
|
||||||
credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """
|
credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """
|
||||||
|
|
||||||
distributed_cost_center_query = """
|
distributed_cost_center_query = """
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT name as gl_entry,
|
SELECT name as gl_entry,
|
||||||
@ -155,7 +158,7 @@ def get_gl_entries(filters, accounting_dimensions):
|
|||||||
against_voucher_type,
|
against_voucher_type,
|
||||||
against_voucher,
|
against_voucher,
|
||||||
account_currency,
|
account_currency,
|
||||||
remarks, against,
|
remarks, against,
|
||||||
is_opening, `tabGL Entry`.creation {select_fields_with_percentage}
|
is_opening, `tabGL Entry`.creation {select_fields_with_percentage}
|
||||||
FROM `tabGL Entry`,
|
FROM `tabGL Entry`,
|
||||||
(
|
(
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
{
|
{
|
||||||
"add_total_row": 0,
|
"add_total_row": 1,
|
||||||
"apply_user_permissions": 1,
|
"creation": "2013-02-25 17:03:34",
|
||||||
"creation": "2013-02-25 17:03:34",
|
"disabled": 0,
|
||||||
"disabled": 0,
|
"docstatus": 0,
|
||||||
"docstatus": 0,
|
"doctype": "Report",
|
||||||
"doctype": "Report",
|
"idx": 3,
|
||||||
"idx": 3,
|
"is_standard": "Yes",
|
||||||
"is_standard": "Yes",
|
"modified": "2020-08-13 11:26:39.112352",
|
||||||
"modified": "2017-02-24 20:12:22.464240",
|
"modified_by": "Administrator",
|
||||||
"modified_by": "Administrator",
|
"module": "Accounts",
|
||||||
"module": "Accounts",
|
"name": "Gross Profit",
|
||||||
"name": "Gross Profit",
|
"owner": "Administrator",
|
||||||
"owner": "Administrator",
|
"ref_doctype": "Sales Invoice",
|
||||||
"ref_doctype": "Sales Invoice",
|
"report_name": "Gross Profit",
|
||||||
"report_name": "Gross Profit",
|
"report_type": "Script Report",
|
||||||
"report_type": "Script Report",
|
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "Accounts Manager"
|
"role": "Accounts Manager"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "Accounts User"
|
"role": "Accounts User"
|
||||||
}
|
}
|
||||||
|
@ -268,9 +268,9 @@ class GrossProfitGenerator(object):
|
|||||||
def get_last_purchase_rate(self, item_code, row):
|
def get_last_purchase_rate(self, item_code, row):
|
||||||
condition = ''
|
condition = ''
|
||||||
if row.project:
|
if row.project:
|
||||||
condition += " AND a.project='%s'" % (row.project)
|
condition += " AND a.project=%s" % (frappe.db.escape(row.project))
|
||||||
elif row.cost_center:
|
elif row.cost_center:
|
||||||
condition += " AND a.cost_center='%s'" % (row.cost_center)
|
condition += " AND a.cost_center=%s" % (frappe.db.escape(row.cost_center))
|
||||||
if self.filters.to_date:
|
if self.filters.to_date:
|
||||||
condition += " AND modified='%s'" % (self.filters.to_date)
|
condition += " AND modified='%s'" % (self.filters.to_date)
|
||||||
|
|
||||||
|
@ -252,13 +252,6 @@ frappe.ui.form.on('Asset', {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
available_for_use_date: function(frm) {
|
|
||||||
$.each(frm.doc.finance_books || [], function(i, d) {
|
|
||||||
if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date;
|
|
||||||
});
|
|
||||||
refresh_field("finance_books");
|
|
||||||
},
|
|
||||||
|
|
||||||
is_existing_asset: function(frm) {
|
is_existing_asset: function(frm) {
|
||||||
frm.trigger("toggle_reference_doc");
|
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));
|
||||||
@ -438,6 +431,15 @@ frappe.ui.form.on('Asset Finance Book', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frappe.flags.dont_change_rate = false;
|
frappe.flags.dont_change_rate = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
depreciation_start_date: function(frm, cdt, cdn) {
|
||||||
|
const book = locals[cdt][cdn];
|
||||||
|
if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) {
|
||||||
|
frappe.msgprint(__(`Depreciation Posting Date should not be equal to Available for Use Date.`));
|
||||||
|
book.depreciation_start_date = "";
|
||||||
|
frm.refresh_field("finance_books");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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, month_diff, add_days
|
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days, get_last_day, get_datetime
|
||||||
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 \
|
||||||
@ -83,6 +83,11 @@ class Asset(AccountsController):
|
|||||||
if not self.available_for_use_date:
|
if not self.available_for_use_date:
|
||||||
frappe.throw(_("Available for use date is required"))
|
frappe.throw(_("Available for use date is required"))
|
||||||
|
|
||||||
|
for d in self.finance_books:
|
||||||
|
if d.depreciation_start_date == self.available_for_use_date:
|
||||||
|
frappe.throw(_("Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.").format(d.idx),
|
||||||
|
title=_("Incorrect Date"))
|
||||||
|
|
||||||
def set_missing_values(self):
|
def set_missing_values(self):
|
||||||
if not self.asset_category:
|
if not self.asset_category:
|
||||||
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
|
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
|
||||||
@ -135,6 +140,10 @@ class Asset(AccountsController):
|
|||||||
def make_asset_movement(self):
|
def make_asset_movement(self):
|
||||||
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
|
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
|
||||||
reference_docname = self.purchase_receipt or self.purchase_invoice
|
reference_docname = self.purchase_receipt or self.purchase_invoice
|
||||||
|
transaction_date = getdate(self.purchase_date)
|
||||||
|
if reference_docname:
|
||||||
|
posting_date, posting_time = frappe.db.get_value(reference_doctype, reference_docname, ["posting_date", "posting_time"])
|
||||||
|
transaction_date = get_datetime("{} {}".format(posting_date, posting_time))
|
||||||
assets = [{
|
assets = [{
|
||||||
'asset': self.name,
|
'asset': self.name,
|
||||||
'asset_name': self.asset_name,
|
'asset_name': self.asset_name,
|
||||||
@ -146,7 +155,7 @@ class Asset(AccountsController):
|
|||||||
'assets': assets,
|
'assets': assets,
|
||||||
'purpose': 'Receipt',
|
'purpose': 'Receipt',
|
||||||
'company': self.company,
|
'company': self.company,
|
||||||
'transaction_date': getdate(nowdate()),
|
'transaction_date': transaction_date,
|
||||||
'reference_doctype': reference_doctype,
|
'reference_doctype': reference_doctype,
|
||||||
'reference_name': reference_docname
|
'reference_name': reference_docname
|
||||||
}).insert()
|
}).insert()
|
||||||
@ -294,7 +303,7 @@ class Asset(AccountsController):
|
|||||||
if not row.depreciation_start_date:
|
if not row.depreciation_start_date:
|
||||||
if not self.available_for_use_date:
|
if not self.available_for_use_date:
|
||||||
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
|
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
|
||||||
row.depreciation_start_date = self.available_for_use_date
|
row.depreciation_start_date = get_last_day(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
|
||||||
|
@ -371,19 +371,18 @@ class TestAsset(unittest.TestCase):
|
|||||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
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)
|
||||||
asset.calculate_depreciation = 1
|
asset.calculate_depreciation = 1
|
||||||
asset.available_for_use_date = nowdate()
|
asset.available_for_use_date = '2020-01-01'
|
||||||
asset.purchase_date = nowdate()
|
asset.purchase_date = '2020-01-01'
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"depreciation_method": "Straight Line",
|
"depreciation_method": "Straight Line",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 10,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 1
|
||||||
"depreciation_start_date": nowdate()
|
|
||||||
})
|
})
|
||||||
asset.insert()
|
asset.insert()
|
||||||
asset.submit()
|
asset.submit()
|
||||||
|
|
||||||
post_depreciation_entries(date=add_months(nowdate(), 10))
|
post_depreciation_entries(date=add_months('2020-01-01', 4))
|
||||||
|
|
||||||
scrap_asset(asset.name)
|
scrap_asset(asset.name)
|
||||||
|
|
||||||
@ -392,9 +391,9 @@ class TestAsset(unittest.TestCase):
|
|||||||
self.assertTrue(asset.journal_entry_for_scrap)
|
self.assertTrue(asset.journal_entry_for_scrap)
|
||||||
|
|
||||||
expected_gle = (
|
expected_gle = (
|
||||||
("_Test Accumulated Depreciations - _TC", 30000.0, 0.0),
|
("_Test Accumulated Depreciations - _TC", 36000.0, 0.0),
|
||||||
("_Test Fixed Asset - _TC", 0.0, 100000.0),
|
("_Test Fixed Asset - _TC", 0.0, 100000.0),
|
||||||
("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0)
|
("_Test Gain/Loss on Asset Disposal - _TC", 64000.0, 0.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||||
@ -466,8 +465,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"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": "2020-06-06"
|
|
||||||
})
|
})
|
||||||
asset.insert()
|
asset.insert()
|
||||||
accumulated_depreciation_after_full_schedule = \
|
accumulated_depreciation_after_full_schedule = \
|
||||||
|
@ -1,347 +1,99 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"creation": "2018-05-08 14:44:37.095570",
|
||||||
"allow_guest_to_view": 0,
|
"doctype": "DocType",
|
||||||
"allow_import": 0,
|
"editable_grid": 1,
|
||||||
"allow_rename": 0,
|
"engine": "InnoDB",
|
||||||
"beta": 0,
|
"field_order": [
|
||||||
"creation": "2018-05-08 14:44:37.095570",
|
"finance_book",
|
||||||
"custom": 0,
|
"depreciation_method",
|
||||||
"docstatus": 0,
|
"total_number_of_depreciations",
|
||||||
"doctype": "DocType",
|
"column_break_5",
|
||||||
"document_type": "",
|
"frequency_of_depreciation",
|
||||||
"editable_grid": 1,
|
"depreciation_start_date",
|
||||||
"engine": "InnoDB",
|
"expected_value_after_useful_life",
|
||||||
|
"value_after_depreciation",
|
||||||
|
"rate_of_depreciation"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "finance_book",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Finance Book",
|
||||||
"collapsible": 0,
|
"options": "Finance Book"
|
||||||
"columns": 0,
|
},
|
||||||
"depends_on": "",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "finance_book",
|
|
||||||
"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": "Finance Book",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "depreciation_method",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Select",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Depreciation Method",
|
||||||
"collapsible": 0,
|
"options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fetch_if_empty": 0,
|
},
|
||||||
"fieldname": "depreciation_method",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"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": "Depreciation Method",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual",
|
|
||||||
"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,
|
"fieldname": "total_number_of_depreciations",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Int",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Total Number of Depreciations",
|
||||||
"collapsible": 0,
|
"reqd": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "total_number_of_depreciations",
|
|
||||||
"fieldtype": "Int",
|
|
||||||
"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": "Total Number of Depreciations",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "column_break_5",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_5",
|
|
||||||
"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,
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "frequency_of_depreciation",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Int",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Frequency of Depreciation (Months)",
|
||||||
"collapsible": 0,
|
"reqd": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "frequency_of_depreciation",
|
|
||||||
"fieldtype": "Int",
|
|
||||||
"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": "Frequency of Depreciation (Months)",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"depends_on": "eval:parent.doctype == 'Asset'",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "depreciation_start_date",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Date",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Depreciation Posting Date",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"depends_on": "eval:parent.doctype == 'Asset'",
|
},
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "depreciation_start_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"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": "Depreciation Start Date",
|
|
||||||
"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,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
"depends_on": "eval:parent.doctype == 'Asset'",
|
||||||
"allow_on_submit": 0,
|
"fieldname": "expected_value_after_useful_life",
|
||||||
"bold": 0,
|
"fieldtype": "Currency",
|
||||||
"collapsible": 0,
|
"label": "Expected Value After Useful Life",
|
||||||
"columns": 0,
|
"options": "Company:company:default_currency"
|
||||||
"default": "0",
|
},
|
||||||
"depends_on": "eval:parent.doctype == 'Asset'",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "expected_value_after_useful_life",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"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": "Expected Value After Useful Life",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"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": "value_after_depreciation",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Currency",
|
||||||
"allow_on_submit": 0,
|
"hidden": 1,
|
||||||
"bold": 0,
|
"label": "Value After Depreciation",
|
||||||
"collapsible": 0,
|
"no_copy": 1,
|
||||||
"columns": 0,
|
"options": "Company:company:default_currency",
|
||||||
"fetch_if_empty": 0,
|
"print_hide": 1,
|
||||||
"fieldname": "value_after_depreciation",
|
"read_only": 1
|
||||||
"fieldtype": "Currency",
|
},
|
||||||
"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": "Value After Depreciation",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"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,
|
"depends_on": "eval:doc.depreciation_method == 'Written Down Value'",
|
||||||
"allow_in_quick_entry": 0,
|
"description": "In Percentage",
|
||||||
"allow_on_submit": 0,
|
"fieldname": "rate_of_depreciation",
|
||||||
"bold": 0,
|
"fieldtype": "Percent",
|
||||||
"collapsible": 0,
|
"label": "Rate of Depreciation"
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "eval:doc.depreciation_method == 'Written Down Value'",
|
|
||||||
"description": "In Percentage",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "rate_of_depreciation",
|
|
||||||
"fieldtype": "Percent",
|
|
||||||
"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": "Rate of Depreciation",
|
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_toolbar": 0,
|
"istable": 1,
|
||||||
"idx": 0,
|
"links": [],
|
||||||
"in_create": 0,
|
"modified": "2020-09-16 12:11:30.631788",
|
||||||
"is_submittable": 0,
|
"modified_by": "Administrator",
|
||||||
"issingle": 0,
|
"module": "Assets",
|
||||||
"istable": 1,
|
"name": "Asset Finance Book",
|
||||||
"max_attachments": 0,
|
"owner": "Administrator",
|
||||||
"modified": "2019-04-09 19:45:14.523488",
|
"permissions": [],
|
||||||
"modified_by": "Administrator",
|
"quick_entry": 1,
|
||||||
"module": "Assets",
|
"sort_field": "modified",
|
||||||
"name": "Asset Finance Book",
|
"sort_order": "DESC",
|
||||||
"name_case": "",
|
"track_changes": 1
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 1,
|
|
||||||
"read_only": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -32,8 +32,7 @@ class TestAssetMovement(unittest.TestCase):
|
|||||||
"next_depreciation_date": "2020-12-31",
|
"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": "2020-06-06"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if asset.docstatus == 0:
|
if asset.docstatus == 0:
|
||||||
@ -82,8 +81,7 @@ class TestAssetMovement(unittest.TestCase):
|
|||||||
"next_depreciation_date": "2020-12-31",
|
"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": "2020-06-06"
|
|
||||||
})
|
})
|
||||||
if asset.docstatus == 0:
|
if asset.docstatus == 0:
|
||||||
asset.submit()
|
asset.submit()
|
||||||
|
@ -1084,7 +1084,7 @@
|
|||||||
"idx": 105,
|
"idx": 105,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-31 14:13:44.610190",
|
"modified": "2020-09-14 14:36:12.418690",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
@ -1135,5 +1135,6 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"timeline_field": "supplier",
|
"timeline_field": "supplier",
|
||||||
"title_field": "supplier"
|
"title_field": "supplier",
|
||||||
|
"track_changes": 1
|
||||||
}
|
}
|
@ -89,7 +89,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
|
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
|
||||||
|
|
||||||
|
|
||||||
def test_update_child_qty_rate(self):
|
def test_update_child(self):
|
||||||
mr = make_material_request(qty=10)
|
mr = make_material_request(qty=10)
|
||||||
po = make_purchase_order(mr.name)
|
po = make_purchase_order(mr.name)
|
||||||
po.supplier = "_Test Supplier"
|
po.supplier = "_Test Supplier"
|
||||||
@ -119,7 +119,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||||
|
|
||||||
|
|
||||||
def test_add_new_item_in_update_child_qty_rate(self):
|
def test_update_child_adding_new_item(self):
|
||||||
po = create_purchase_order(do_not_save=1)
|
po = create_purchase_order(do_not_save=1)
|
||||||
po.items[0].qty = 4
|
po.items[0].qty = 4
|
||||||
po.save()
|
po.save()
|
||||||
@ -145,7 +145,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEqual(po.status, 'To Receive and Bill')
|
self.assertEqual(po.status, 'To Receive and Bill')
|
||||||
|
|
||||||
|
|
||||||
def test_remove_item_in_update_child_qty_rate(self):
|
def test_update_child_removing_item(self):
|
||||||
po = create_purchase_order(do_not_save=1)
|
po = create_purchase_order(do_not_save=1)
|
||||||
po.items[0].qty = 4
|
po.items[0].qty = 4
|
||||||
po.save()
|
po.save()
|
||||||
@ -185,7 +185,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEquals(len(po.get('items')), 1)
|
self.assertEquals(len(po.get('items')), 1)
|
||||||
self.assertEqual(po.status, 'To Receive and Bill')
|
self.assertEqual(po.status, 'To Receive and Bill')
|
||||||
|
|
||||||
def test_update_child_qty_rate_perm(self):
|
def test_update_child_perm(self):
|
||||||
po = create_purchase_order(item_code= "_Test Item", qty=4)
|
po = create_purchase_order(item_code= "_Test Item", qty=4)
|
||||||
|
|
||||||
user = 'test@example.com'
|
user = 'test@example.com'
|
||||||
@ -202,6 +202,72 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
|
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name)
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
|
|
||||||
|
def test_update_child_with_tax_template(self):
|
||||||
|
tax_template = "_Test Account Excise Duty @ 10"
|
||||||
|
item = "_Test Item Home Desktop 100"
|
||||||
|
|
||||||
|
if not frappe.db.exists("Item Tax", {"parent":item, "item_tax_template":tax_template}):
|
||||||
|
item_doc = frappe.get_doc("Item", item)
|
||||||
|
item_doc.append("taxes", {
|
||||||
|
"item_tax_template": tax_template,
|
||||||
|
"valid_from": nowdate()
|
||||||
|
})
|
||||||
|
item_doc.save()
|
||||||
|
else:
|
||||||
|
# update valid from
|
||||||
|
frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = CURDATE()
|
||||||
|
where parent = %(item)s and item_tax_template = %(tax)s""",
|
||||||
|
{"item": item, "tax": tax_template})
|
||||||
|
|
||||||
|
po = create_purchase_order(item_code=item, qty=1, do_not_save=1)
|
||||||
|
|
||||||
|
po.append("taxes", {
|
||||||
|
"account_head": "_Test Account Excise Duty - _TC",
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Excise Duty",
|
||||||
|
"doctype": "Purchase Taxes and Charges",
|
||||||
|
"rate": 10
|
||||||
|
})
|
||||||
|
po.insert()
|
||||||
|
po.submit()
|
||||||
|
|
||||||
|
self.assertEqual(po.taxes[0].tax_amount, 50)
|
||||||
|
self.assertEqual(po.taxes[0].total, 550)
|
||||||
|
|
||||||
|
items = json.dumps([
|
||||||
|
{'item_code' : item, 'rate' : 500, 'qty' : 1, 'docname': po.items[0].name},
|
||||||
|
{'item_code' : item, 'rate' : 100, 'qty' : 1} # added item
|
||||||
|
])
|
||||||
|
update_child_qty_rate('Purchase Order', items, po.name)
|
||||||
|
|
||||||
|
po.reload()
|
||||||
|
self.assertEqual(po.taxes[0].tax_amount, 60)
|
||||||
|
self.assertEqual(po.taxes[0].total, 660)
|
||||||
|
|
||||||
|
frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = NULL
|
||||||
|
where parent = %(item)s and item_tax_template = %(tax)s""",
|
||||||
|
{"item": item, "tax": tax_template})
|
||||||
|
|
||||||
|
def test_update_child_uom_conv_factor_change(self):
|
||||||
|
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
|
||||||
|
total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])
|
||||||
|
|
||||||
|
trans_item = json.dumps([{
|
||||||
|
'item_code': po.get("items")[0].item_code,
|
||||||
|
'rate': po.get("items")[0].rate,
|
||||||
|
'qty': po.get("items")[0].qty,
|
||||||
|
'uom': "_Test UOM 1",
|
||||||
|
'conversion_factor': 2,
|
||||||
|
'docname': po.get("items")[0].name
|
||||||
|
}])
|
||||||
|
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||||
|
po.reload()
|
||||||
|
|
||||||
|
total_reqd_qty_after_change = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])
|
||||||
|
|
||||||
|
self.assertEqual(total_reqd_qty_after_change, 2 * total_reqd_qty)
|
||||||
|
|
||||||
def test_update_qty(self):
|
def test_update_qty(self):
|
||||||
po = create_purchase_order()
|
po = create_purchase_order()
|
||||||
|
|
||||||
|
@ -148,11 +148,11 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-12 15:43:53.862897",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item Supplied",
|
"name": "Purchase Order Item Supplied",
|
||||||
"owner": "dhanalekshmi@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC"
|
||||||
|
@ -188,11 +188,11 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-10 18:09:33.997618",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Receipt Item Supplied",
|
"name": "Purchase Receipt Item Supplied",
|
||||||
"owner": "wasim@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
@ -11,6 +11,8 @@ from erpnext.stock.doctype.item.test_item import make_item
|
|||||||
from erpnext.templates.pages.rfq import check_supplier_has_docname_access
|
from erpnext.templates.pages.rfq import check_supplier_has_docname_access
|
||||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
|
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
|
||||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
|
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
|
||||||
|
from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
|
||||||
|
from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
|
||||||
|
|
||||||
class TestRequestforQuotation(unittest.TestCase):
|
class TestRequestforQuotation(unittest.TestCase):
|
||||||
def test_quote_status(self):
|
def test_quote_status(self):
|
||||||
@ -110,6 +112,23 @@ class TestRequestforQuotation(unittest.TestCase):
|
|||||||
self.assertEqual(supplier_quotation.items[0].qty, 5)
|
self.assertEqual(supplier_quotation.items[0].qty, 5)
|
||||||
self.assertEqual(supplier_quotation.items[0].stock_qty, 10)
|
self.assertEqual(supplier_quotation.items[0].stock_qty, 10)
|
||||||
|
|
||||||
|
def test_make_rfq_from_opportunity(self):
|
||||||
|
opportunity = make_opportunity(with_items=1)
|
||||||
|
supplier_data = get_supplier_data()
|
||||||
|
rfq = make_rfq(opportunity.name)
|
||||||
|
|
||||||
|
self.assertEqual(len(rfq.get("items")), len(opportunity.get("items")))
|
||||||
|
rfq.message_for_supplier = 'Please supply the specified items at the best possible rates.'
|
||||||
|
|
||||||
|
for item in rfq.items:
|
||||||
|
item.warehouse = "_Test Warehouse - _TC"
|
||||||
|
|
||||||
|
for data in supplier_data:
|
||||||
|
rfq.append('suppliers', data)
|
||||||
|
|
||||||
|
rfq.status = 'Draft'
|
||||||
|
rfq.submit()
|
||||||
|
|
||||||
def make_request_for_quotation(**args):
|
def make_request_for_quotation(**args):
|
||||||
"""
|
"""
|
||||||
:param supplier_data: List containing supplier data
|
:param supplier_data: List containing supplier data
|
||||||
|
@ -178,6 +178,7 @@ def make_all_scorecards(docname):
|
|||||||
period_card = make_supplier_scorecard(docname, None)
|
period_card = make_supplier_scorecard(docname, None)
|
||||||
period_card.start_date = start_date
|
period_card.start_date = start_date
|
||||||
period_card.end_date = end_date
|
period_card.end_date = end_date
|
||||||
|
period_card.insert(ignore_permissions=True)
|
||||||
period_card.submit()
|
period_card.submit()
|
||||||
scp_count = scp_count + 1
|
scp_count = scp_count + 1
|
||||||
if start_date < first_start_date:
|
if start_date < first_start_date:
|
||||||
|
@ -106,7 +106,7 @@ def make_supplier_scorecard(source_name, target_doc=None):
|
|||||||
"doctype": "Supplier Scorecard Scoring Criteria",
|
"doctype": "Supplier Scorecard Scoring Criteria",
|
||||||
"postprocess": update_criteria_fields,
|
"postprocess": update_criteria_fields,
|
||||||
}
|
}
|
||||||
}, target_doc, post_process)
|
}, target_doc, post_process, ignore_permissions=True)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
@ -12,7 +12,22 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
reqd: 1,
|
"fieldname":"from_date",
|
||||||
|
"label": __("From Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": "80",
|
||||||
|
"reqd": 1,
|
||||||
|
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_date",
|
||||||
|
"label": __("To Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": "80",
|
||||||
|
"reqd": 1,
|
||||||
|
"default": frappe.datetime.get_today()
|
||||||
|
},
|
||||||
|
{
|
||||||
default: "",
|
default: "",
|
||||||
options: "Item",
|
options: "Item",
|
||||||
label: __("Item"),
|
label: __("Item"),
|
||||||
@ -45,13 +60,12 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldtype: "Link",
|
fieldtype: "MultiSelectList",
|
||||||
label: __("Supplier Quotation"),
|
label: __("Supplier Quotation"),
|
||||||
options: "Supplier Quotation",
|
|
||||||
fieldname: "supplier_quotation",
|
fieldname: "supplier_quotation",
|
||||||
default: "",
|
default: "",
|
||||||
get_query: () => {
|
get_data: function(txt) {
|
||||||
return { filters: { "docstatus": ["<", 2] } }
|
return frappe.db.get_link_options('Supplier Quotation', txt, {'docstatus': ["<", 2]});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -63,9 +77,30 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
|||||||
get_query: () => {
|
get_query: () => {
|
||||||
return { filters: { "docstatus": ["<", 2] } }
|
return { filters: { "docstatus": ["<", 2] } }
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Check",
|
||||||
|
label: __("Include Expired"),
|
||||||
|
fieldname: "include_expired",
|
||||||
|
default: 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
formatter: (value, row, column, data, default_formatter) => {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
|
||||||
|
if(column.fieldname === "valid_till" && data.valid_till){
|
||||||
|
if(frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 1){
|
||||||
|
value = `<div style="color:red">${value}</div>`;
|
||||||
|
}
|
||||||
|
else if (frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 7){
|
||||||
|
value = `<div style="color:darkorange">${value}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
onload: (report) => {
|
onload: (report) => {
|
||||||
// Create a button for setting the default supplier
|
// Create a button for setting the default supplier
|
||||||
report.page.add_inner_button(__("Select Default Supplier"), () => {
|
report.page.add_inner_button(__("Select Default Supplier"), () => {
|
||||||
|
@ -16,44 +16,49 @@ def execute(filters=None):
|
|||||||
supplier_quotation_data = get_data(filters, conditions)
|
supplier_quotation_data = get_data(filters, conditions)
|
||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
|
|
||||||
data, chart_data = prepare_data(supplier_quotation_data)
|
data, chart_data = prepare_data(supplier_quotation_data, filters)
|
||||||
|
message = get_message()
|
||||||
|
|
||||||
return columns, data, None, chart_data
|
return columns, data, message, chart_data
|
||||||
|
|
||||||
def get_conditions(filters):
|
def get_conditions(filters):
|
||||||
conditions = ""
|
conditions = ""
|
||||||
|
if filters.get("item_code"):
|
||||||
|
conditions += " AND sqi.item_code = %(item_code)s"
|
||||||
|
|
||||||
if filters.get("supplier_quotation"):
|
if filters.get("supplier_quotation"):
|
||||||
conditions += " AND sqi.parent = %(supplier_quotation)s"
|
conditions += " AND sqi.parent in %(supplier_quotation)s"
|
||||||
|
|
||||||
if filters.get("request_for_quotation"):
|
if filters.get("request_for_quotation"):
|
||||||
conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s"
|
conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s"
|
||||||
|
|
||||||
if filters.get("supplier"):
|
if filters.get("supplier"):
|
||||||
conditions += " AND sq.supplier in %(supplier)s"
|
conditions += " AND sq.supplier in %(supplier)s"
|
||||||
|
|
||||||
|
if not filters.get("include_expired"):
|
||||||
|
conditions += " AND sq.status != 'Expired'"
|
||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_data(filters, conditions):
|
def get_data(filters, conditions):
|
||||||
if not filters.get("item_code"):
|
|
||||||
return []
|
|
||||||
|
|
||||||
supplier_quotation_data = frappe.db.sql("""SELECT
|
supplier_quotation_data = frappe.db.sql("""SELECT
|
||||||
sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
|
sqi.parent, sqi.item_code, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
|
||||||
sq.supplier
|
sqi.lead_time_days, sq.supplier, sq.valid_till
|
||||||
FROM
|
FROM
|
||||||
`tabSupplier Quotation Item` sqi,
|
`tabSupplier Quotation Item` sqi,
|
||||||
`tabSupplier Quotation` sq
|
`tabSupplier Quotation` sq
|
||||||
WHERE
|
WHERE
|
||||||
sqi.item_code = %(item_code)s
|
sqi.parent = sq.name
|
||||||
AND sqi.parent = sq.name
|
|
||||||
AND sqi.docstatus < 2
|
AND sqi.docstatus < 2
|
||||||
AND sq.company = %(company)s
|
AND sq.company = %(company)s
|
||||||
AND sq.status != 'Expired'
|
AND sq.transaction_date between %(from_date)s and %(to_date)s
|
||||||
{0}""".format(conditions), filters, as_dict=1)
|
{0}
|
||||||
|
order by sq.transaction_date, sqi.item_code""".format(conditions), filters, as_dict=1)
|
||||||
|
|
||||||
return supplier_quotation_data
|
return supplier_quotation_data
|
||||||
|
|
||||||
def prepare_data(supplier_quotation_data):
|
def prepare_data(supplier_quotation_data, filters):
|
||||||
out, suppliers, qty_list = [], [], []
|
out, suppliers, qty_list, chart_data = [], [], [], []
|
||||||
supplier_wise_map = defaultdict(list)
|
supplier_wise_map = defaultdict(list)
|
||||||
supplier_qty_price_map = {}
|
supplier_qty_price_map = {}
|
||||||
|
|
||||||
@ -70,20 +75,24 @@ def prepare_data(supplier_quotation_data):
|
|||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
|
|
||||||
row = {
|
row = {
|
||||||
|
"item_code": data.get('item_code'),
|
||||||
"quotation": data.get("parent"),
|
"quotation": data.get("parent"),
|
||||||
"qty": data.get("qty"),
|
"qty": data.get("qty"),
|
||||||
"price": flt(data.get("rate") * exchange_rate, float_precision),
|
"price": flt(data.get("rate") * exchange_rate, float_precision),
|
||||||
"uom": data.get("uom"),
|
"uom": data.get("uom"),
|
||||||
"request_for_quotation": data.get("request_for_quotation"),
|
"request_for_quotation": data.get("request_for_quotation"),
|
||||||
|
"valid_till": data.get('valid_till'),
|
||||||
|
"lead_time_days": data.get('lead_time_days')
|
||||||
}
|
}
|
||||||
|
|
||||||
# map for report view of form {'supplier1':[{},{},...]}
|
# map for report view of form {'supplier1':[{},{},...]}
|
||||||
supplier_wise_map[supplier].append(row)
|
supplier_wise_map[supplier].append(row)
|
||||||
|
|
||||||
# map for chart preparation of the form {'supplier1': {'qty': 'price'}}
|
# map for chart preparation of the form {'supplier1': {'qty': 'price'}}
|
||||||
if not supplier in supplier_qty_price_map:
|
if filters.get("item_code"):
|
||||||
supplier_qty_price_map[supplier] = {}
|
if not supplier in supplier_qty_price_map:
|
||||||
supplier_qty_price_map[supplier][row["qty"]] = row["price"]
|
supplier_qty_price_map[supplier] = {}
|
||||||
|
supplier_qty_price_map[supplier][row["qty"]] = row["price"]
|
||||||
|
|
||||||
suppliers.append(supplier)
|
suppliers.append(supplier)
|
||||||
qty_list.append(data.get("qty"))
|
qty_list.append(data.get("qty"))
|
||||||
@ -97,7 +106,8 @@ def prepare_data(supplier_quotation_data):
|
|||||||
for entry in supplier_wise_map[supplier]:
|
for entry in supplier_wise_map[supplier]:
|
||||||
out.append(entry)
|
out.append(entry)
|
||||||
|
|
||||||
chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
|
if filters.get("item_code"):
|
||||||
|
chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
|
||||||
|
|
||||||
return out, chart_data
|
return out, chart_data
|
||||||
|
|
||||||
@ -117,9 +127,10 @@ def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map):
|
|||||||
data_points_map[qty].append(None)
|
data_points_map[qty].append(None)
|
||||||
|
|
||||||
dataset = []
|
dataset = []
|
||||||
|
currency_symbol = frappe.db.get_value("Currency", frappe.db.get_default("currency"), "symbol")
|
||||||
for qty in qty_list:
|
for qty in qty_list:
|
||||||
datapoints = {
|
datapoints = {
|
||||||
"name": _("Price for Qty ") + str(qty),
|
"name": currency_symbol + " (Qty " + str(qty) + " )",
|
||||||
"values": data_points_map[qty]
|
"values": data_points_map[qty]
|
||||||
}
|
}
|
||||||
dataset.append(datapoints)
|
dataset.append(datapoints)
|
||||||
@ -140,14 +151,21 @@ def get_columns():
|
|||||||
"label": _("Supplier"),
|
"label": _("Supplier"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier",
|
"options": "Supplier",
|
||||||
|
"width": 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"label": _("Item"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
"width": 200
|
"width": 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "quotation",
|
"fieldname": "uom",
|
||||||
"label": _("Supplier Quotation"),
|
"label": _("UOM"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier Quotation",
|
"options": "UOM",
|
||||||
"width": 200
|
"width": 90
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "qty",
|
"fieldname": "qty",
|
||||||
@ -163,19 +181,43 @@ def get_columns():
|
|||||||
"width": 110
|
"width": 110
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "uom",
|
"fieldname": "quotation",
|
||||||
"label": _("UOM"),
|
"label": _("Supplier Quotation"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "UOM",
|
"options": "Supplier Quotation",
|
||||||
"width": 90
|
"width": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "valid_till",
|
||||||
|
"label": _("Valid Till"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "lead_time_days",
|
||||||
|
"label": _("Lead Time (Days)"),
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"width": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "request_for_quotation",
|
"fieldname": "request_for_quotation",
|
||||||
"label": _("Request for Quotation"),
|
"label": _("Request for Quotation"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Request for Quotation",
|
"options": "Request for Quotation",
|
||||||
"width": 200
|
"width": 150
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
|
def get_message():
|
||||||
|
return """<span class="indicator">
|
||||||
|
Valid till :
|
||||||
|
</span>
|
||||||
|
<span class="indicator orange">
|
||||||
|
Expires in a week or less
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="indicator red">
|
||||||
|
Expires today / Already Expired
|
||||||
|
</span>"""
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "field:id",
|
"autoname": "field:id",
|
||||||
"creation": "2019-06-05 12:07:02.634534",
|
"creation": "2019-06-05 12:07:02.634534",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -14,6 +15,7 @@
|
|||||||
"contact",
|
"contact",
|
||||||
"contact_name",
|
"contact_name",
|
||||||
"column_break_10",
|
"column_break_10",
|
||||||
|
"customer",
|
||||||
"lead",
|
"lead",
|
||||||
"lead_name",
|
"lead_name",
|
||||||
"section_break_5",
|
"section_break_5",
|
||||||
@ -28,7 +30,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "section_break_5",
|
"fieldname": "section_break_5",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Call Details"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "id",
|
"fieldname": "id",
|
||||||
@ -125,10 +128,19 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Lead Name",
|
"label": "Lead Name",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Customer",
|
||||||
|
"options": "Customer",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"modified": "2019-08-06 05:46:53.144683",
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-08-25 17:08:34.085731",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Communication",
|
"module": "Communication",
|
||||||
"name": "Call Log",
|
"name": "Call Log",
|
||||||
|
@ -16,6 +16,9 @@ class CallLog(Document):
|
|||||||
self.contact = get_contact_with_phone_number(number)
|
self.contact = get_contact_with_phone_number(number)
|
||||||
self.lead = get_lead_with_phone_number(number)
|
self.lead = get_lead_with_phone_number(number)
|
||||||
|
|
||||||
|
contact = frappe.get_doc("Contact", self.contact)
|
||||||
|
self.customer = contact.get_link_for("Customer")
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
self.trigger_call_popup()
|
self.trigger_call_popup()
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import json
|
|||||||
from frappe import _, throw
|
from frappe import _, throw
|
||||||
from frappe.utils import (today, flt, cint, fmt_money, formatdate,
|
from frappe.utils import (today, flt, cint, fmt_money, formatdate,
|
||||||
getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form)
|
getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form)
|
||||||
|
from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied, WorkflowPermissionError
|
||||||
from erpnext.stock.get_item_details import get_conversion_factor, get_item_details
|
from erpnext.stock.get_item_details import get_conversion_factor, get_item_details
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
|
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
|
||||||
@ -19,7 +20,7 @@ from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_t
|
|||||||
from erpnext.exceptions import InvalidCurrency
|
from erpnext.exceptions import InvalidCurrency
|
||||||
from six import text_type
|
from six import text_type
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
from erpnext.stock.get_item_details import get_item_warehouse
|
from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
|
||||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||||
|
|
||||||
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
|
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
|
||||||
@ -1157,6 +1158,18 @@ def get_supplier_block_status(party_name):
|
|||||||
}
|
}
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
def set_child_tax_template_and_map(item, child_item, parent_doc):
|
||||||
|
args = {
|
||||||
|
'item_code': item.item_code,
|
||||||
|
'posting_date': parent_doc.transaction_date,
|
||||||
|
'tax_category': parent_doc.get('tax_category'),
|
||||||
|
'company': parent_doc.get('company')
|
||||||
|
}
|
||||||
|
|
||||||
|
child_item.item_tax_template = _get_item_tax_template(args, item.taxes)
|
||||||
|
if child_item.get("item_tax_template"):
|
||||||
|
child_item.item_tax_rate = get_item_tax_map(parent_doc.get('company'), child_item.item_tax_template, as_json=True)
|
||||||
|
|
||||||
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
|
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
|
||||||
"""
|
"""
|
||||||
Returns a Sales Order Item child item containing the default values
|
Returns a Sales Order Item child item containing the default values
|
||||||
@ -1168,8 +1181,10 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname,
|
|||||||
child_item.item_name = item.item_name
|
child_item.item_name = item.item_name
|
||||||
child_item.description = item.description
|
child_item.description = item.description
|
||||||
child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
|
child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date
|
||||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
|
child_item.uom = trans_item.get("uom") or item.stock_uom
|
||||||
child_item.uom = item.stock_uom
|
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
||||||
|
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
||||||
|
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||||
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||||
if not child_item.warehouse:
|
if not child_item.warehouse:
|
||||||
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
||||||
@ -1188,13 +1203,15 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
|
|||||||
child_item.item_name = item.item_name
|
child_item.item_name = item.item_name
|
||||||
child_item.description = item.description
|
child_item.description = item.description
|
||||||
child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
|
child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date
|
||||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0
|
child_item.uom = trans_item.get("uom") or item.stock_uom
|
||||||
child_item.uom = item.stock_uom
|
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
||||||
|
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
||||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
||||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||||
|
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||||
return child_item
|
return child_item
|
||||||
|
|
||||||
def check_and_delete_children(parent, data):
|
def validate_and_delete_children(parent, data):
|
||||||
deleted_children = []
|
deleted_children = []
|
||||||
updated_item_names = [d.get("docname") for d in data]
|
updated_item_names = [d.get("docname") for d in data]
|
||||||
for item in parent.items:
|
for item in parent.items:
|
||||||
@ -1221,18 +1238,40 @@ def check_and_delete_children(parent, data):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
||||||
def check_permissions(doc, perm_type='create'):
|
def check_doc_permissions(doc, perm_type='create'):
|
||||||
try:
|
try:
|
||||||
doc.check_permission(perm_type)
|
doc.check_permission(perm_type)
|
||||||
except:
|
except frappe.PermissionError:
|
||||||
action = "add" if perm_type == 'create' else "update"
|
actions = { 'create': 'add', 'write': 'update', 'cancel': 'remove' }
|
||||||
frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions"))
|
|
||||||
|
frappe.throw(_("You do not have permissions to {} items in a {}.")
|
||||||
|
.format(actions[perm_type], parent_doctype), title=_("Insufficient Permissions"))
|
||||||
|
|
||||||
|
def validate_workflow_conditions(doc):
|
||||||
|
workflow = get_workflow_name(doc.doctype)
|
||||||
|
if not workflow:
|
||||||
|
return
|
||||||
|
|
||||||
|
workflow_doc = frappe.get_doc("Workflow", workflow)
|
||||||
|
current_state = doc.get(workflow_doc.workflow_state_field)
|
||||||
|
roles = frappe.get_roles()
|
||||||
|
|
||||||
|
transitions = []
|
||||||
|
for transition in workflow_doc.transitions:
|
||||||
|
if transition.next_state == current_state and transition.allowed in roles:
|
||||||
|
if not is_transition_condition_satisfied(transition, doc):
|
||||||
|
continue
|
||||||
|
transitions.append(transition.as_dict())
|
||||||
|
|
||||||
|
if not transitions:
|
||||||
|
frappe.throw(
|
||||||
|
_("You are not allowed to update as per the conditions set in {} Workflow.").format(get_link_to_form("Workflow", workflow)),
|
||||||
|
title=_("Insufficient Permissions")
|
||||||
|
)
|
||||||
|
|
||||||
def get_new_child_item(item_row):
|
def get_new_child_item(item_row):
|
||||||
if parent_doctype == "Sales Order":
|
new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults
|
||||||
return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
|
return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row)
|
||||||
if parent_doctype == "Purchase Order":
|
|
||||||
return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
|
|
||||||
|
|
||||||
def validate_quantity(child_item, d):
|
def validate_quantity(child_item, d):
|
||||||
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
|
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
|
||||||
@ -1246,21 +1285,23 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
||||||
parent = frappe.get_doc(parent_doctype, parent_doctype_name)
|
parent = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||||
|
|
||||||
check_and_delete_children(parent, data)
|
check_doc_permissions(parent, 'cancel')
|
||||||
|
validate_and_delete_children(parent, data)
|
||||||
|
|
||||||
for d in data:
|
for d in data:
|
||||||
new_child_flag = False
|
new_child_flag = False
|
||||||
if not d.get("docname"):
|
if not d.get("docname"):
|
||||||
new_child_flag = True
|
new_child_flag = True
|
||||||
check_permissions(parent, 'create')
|
check_doc_permissions(parent, 'create')
|
||||||
child_item = get_new_child_item(d)
|
child_item = get_new_child_item(d)
|
||||||
else:
|
else:
|
||||||
check_permissions(parent, 'write')
|
check_doc_permissions(parent, 'write')
|
||||||
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
|
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
|
||||||
|
|
||||||
prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
|
prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate"))
|
||||||
prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
|
prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty"))
|
||||||
prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
|
prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor"))
|
||||||
|
prev_uom, new_uom = child_item.get("uom"), d.get("uom")
|
||||||
|
|
||||||
if parent_doctype == 'Sales Order':
|
if parent_doctype == 'Sales Order':
|
||||||
prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
|
prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
|
||||||
@ -1269,9 +1310,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
|
|
||||||
rate_unchanged = prev_rate == new_rate
|
rate_unchanged = prev_rate == new_rate
|
||||||
qty_unchanged = prev_qty == new_qty
|
qty_unchanged = prev_qty == new_qty
|
||||||
|
uom_unchanged = prev_uom == new_uom
|
||||||
conversion_factor_unchanged = prev_con_fac == new_con_fac
|
conversion_factor_unchanged = prev_con_fac == new_con_fac
|
||||||
date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
|
date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc
|
||||||
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged:
|
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
validate_quantity(child_item, d)
|
validate_quantity(child_item, d)
|
||||||
@ -1291,6 +1333,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
else:
|
else:
|
||||||
child_item.conversion_factor = flt(d.get('conversion_factor'))
|
child_item.conversion_factor = flt(d.get('conversion_factor'))
|
||||||
|
|
||||||
|
if d.get("uom"):
|
||||||
|
child_item.uom = d.get("uom")
|
||||||
|
conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor"))
|
||||||
|
child_item.conversion_factor = flt(d.get('conversion_factor')) or conversion_factor
|
||||||
|
|
||||||
if d.get("delivery_date") and parent_doctype == 'Sales Order':
|
if d.get("delivery_date") and parent_doctype == 'Sales Order':
|
||||||
child_item.delivery_date = d.get('delivery_date')
|
child_item.delivery_date = d.get('delivery_date')
|
||||||
|
|
||||||
@ -1356,12 +1403,17 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
parent.update_receiving_percentage()
|
parent.update_receiving_percentage()
|
||||||
if parent.is_subcontracted == "Yes":
|
if parent.is_subcontracted == "Yes":
|
||||||
parent.update_reserved_qty_for_subcontract()
|
parent.update_reserved_qty_for_subcontract()
|
||||||
|
parent.create_raw_materials_supplied("supplied_items")
|
||||||
|
parent.save()
|
||||||
else:
|
else:
|
||||||
parent.update_reserved_qty()
|
parent.update_reserved_qty()
|
||||||
parent.update_project()
|
parent.update_project()
|
||||||
parent.update_prevdoc_status('submit')
|
parent.update_prevdoc_status('submit')
|
||||||
parent.update_delivery_status()
|
parent.update_delivery_status()
|
||||||
|
|
||||||
|
parent.reload()
|
||||||
|
validate_workflow_conditions(parent)
|
||||||
|
|
||||||
parent.update_blanket_order()
|
parent.update_blanket_order()
|
||||||
parent.update_billing_percentage()
|
parent.update_billing_percentage()
|
||||||
parent.set_status()
|
parent.set_status()
|
||||||
|
@ -165,9 +165,14 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
AND company = %(company)s
|
AND company = %(company)s
|
||||||
AND account_currency = %(currency)s
|
AND account_currency = %(currency)s
|
||||||
AND `{searchfield}` LIKE %(txt)s
|
AND `{searchfield}` LIKE %(txt)s
|
||||||
|
{mcond}
|
||||||
ORDER BY idx DESC, name
|
ORDER BY idx DESC, name
|
||||||
LIMIT %(offset)s, %(limit)s
|
LIMIT %(offset)s, %(limit)s
|
||||||
""".format(account_type_condition=account_type_condition, searchfield=searchfield),
|
""".format(
|
||||||
|
account_type_condition=account_type_condition,
|
||||||
|
searchfield=searchfield,
|
||||||
|
mcond=get_match_cond(doctype)
|
||||||
|
),
|
||||||
dict(
|
dict(
|
||||||
account_types=filters.get("account_type"),
|
account_types=filters.get("account_type"),
|
||||||
company=filters.get("company"),
|
company=filters.get("company"),
|
||||||
@ -359,9 +364,21 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
if filters.get("is_return"):
|
if filters.get("is_return"):
|
||||||
having_clause = ""
|
having_clause = ""
|
||||||
|
|
||||||
|
meta = frappe.get_meta("Batch", cached=True)
|
||||||
|
searchfields = meta.get_search_fields()
|
||||||
|
|
||||||
|
search_columns = ''
|
||||||
|
if searchfields:
|
||||||
|
search_columns = ", " + ", ".join(searchfields)
|
||||||
|
|
||||||
if args.get('warehouse'):
|
if args.get('warehouse'):
|
||||||
|
searchfields = ['batch.' + field for field in searchfields]
|
||||||
|
if searchfields:
|
||||||
|
search_columns = ", " + ", ".join(searchfields)
|
||||||
|
|
||||||
batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
|
batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
|
||||||
concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
|
concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
|
||||||
|
{search_columns}
|
||||||
from `tabStock Ledger Entry` sle
|
from `tabStock Ledger Entry` sle
|
||||||
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
|
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
|
||||||
where
|
where
|
||||||
@ -377,6 +394,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
group by batch_no {having_clause}
|
group by batch_no {having_clause}
|
||||||
order by batch.expiry_date, sle.batch_no desc
|
order by batch.expiry_date, sle.batch_no desc
|
||||||
limit %(start)s, %(page_len)s""".format(
|
limit %(start)s, %(page_len)s""".format(
|
||||||
|
search_columns = search_columns,
|
||||||
cond=cond,
|
cond=cond,
|
||||||
match_conditions=get_match_cond(doctype),
|
match_conditions=get_match_cond(doctype),
|
||||||
having_clause = having_clause
|
having_clause = having_clause
|
||||||
@ -384,7 +402,9 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
return batch_nos
|
return batch_nos
|
||||||
else:
|
else:
|
||||||
return frappe.db.sql("""select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date) from `tabBatch` batch
|
return frappe.db.sql("""select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date)
|
||||||
|
{search_columns}
|
||||||
|
from `tabBatch` batch
|
||||||
where batch.disabled = 0
|
where batch.disabled = 0
|
||||||
and item = %(item_code)s
|
and item = %(item_code)s
|
||||||
and (name like %(txt)s
|
and (name like %(txt)s
|
||||||
@ -394,7 +414,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
{0}
|
{0}
|
||||||
{match_conditions}
|
{match_conditions}
|
||||||
order by expiry_date, name desc
|
order by expiry_date, name desc
|
||||||
limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args)
|
limit %(start)s, %(page_len)s""".format(cond, search_columns = search_columns, match_conditions=get_match_cond(doctype)), args)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
@ -242,7 +242,8 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
'type': data.type,
|
'type': data.type,
|
||||||
'amount': -1 * paid_amount,
|
'amount': -1 * paid_amount,
|
||||||
'base_amount': -1 * base_paid_amount,
|
'base_amount': -1 * base_paid_amount,
|
||||||
'account': data.account
|
'account': data.account,
|
||||||
|
'default': data.default
|
||||||
})
|
})
|
||||||
if doc.is_pos:
|
if doc.is_pos:
|
||||||
doc.paid_amount = -1 * source.paid_amount
|
doc.paid_amount = -1 * source.paid_amount
|
||||||
|
@ -81,6 +81,7 @@ class SellingController(StockController):
|
|||||||
party_details = _get_party_details(customer,
|
party_details = _get_party_details(customer,
|
||||||
ignore_permissions=self.flags.ignore_permissions,
|
ignore_permissions=self.flags.ignore_permissions,
|
||||||
doctype=self.doctype, company=self.company,
|
doctype=self.doctype, company=self.company,
|
||||||
|
posting_date=self.get('posting_date'),
|
||||||
fetch_payment_terms_template=fetch_payment_terms_template,
|
fetch_payment_terms_template=fetch_payment_terms_template,
|
||||||
party_address=self.customer_address, shipping_address=self.shipping_address_name)
|
party_address=self.customer_address, shipping_address=self.shipping_address_name)
|
||||||
if not self.meta.get_field("sales_team"):
|
if not self.meta.get_field("sales_team"):
|
||||||
|
@ -255,7 +255,7 @@ class StatusUpdater(Document):
|
|||||||
args['second_source_condition'] = """ + ifnull((select sum(%(second_source_field)s)
|
args['second_source_condition'] = """ + ifnull((select sum(%(second_source_field)s)
|
||||||
from `tab%(second_source_dt)s`
|
from `tab%(second_source_dt)s`
|
||||||
where `%(second_join_field)s`="%(detail_id)s"
|
where `%(second_join_field)s`="%(detail_id)s"
|
||||||
and (`tab%(second_source_dt)s`.docstatus=1) %(second_source_extra_cond)s), 0) """ % args
|
and (`tab%(second_source_dt)s`.docstatus=1) %(second_source_extra_cond)s FOR UPDATE), 0)""" % args
|
||||||
|
|
||||||
if args['detail_id']:
|
if args['detail_id']:
|
||||||
if not args.get("extra_cond"): args["extra_cond"] = ""
|
if not args.get("extra_cond"): args["extra_cond"] = ""
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Reports",
|
"label": "Reports",
|
||||||
"links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Prospects Engaged But Not Converted\",\n \"name\": \"Prospects Engaged But Not Converted\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Opportunity\"\n ],\n \"doctype\": \"Opportunity\",\n \"is_query_report\": true,\n \"label\": \"Minutes to First Response for Opportunity\",\n \"name\": \"Minutes to First Response for Opportunity\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Campaign Efficiency\",\n \"name\": \"Campaign Efficiency\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Owner Efficiency\",\n \"name\": \"Lead Owner Efficiency\",\n \"type\": \"report\"\n }\n]"
|
"links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Prospects Engaged But Not Converted\",\n \"name\": \"Prospects Engaged But Not Converted\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Opportunity\"\n ],\n \"doctype\": \"Opportunity\",\n \"is_query_report\": true,\n \"label\": \"First Response Time for Opportunity\",\n \"name\": \"First Response Time for Opportunity\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Campaign Efficiency\",\n \"name\": \"Campaign Efficiency\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Owner Efficiency\",\n \"name\": \"Lead Owner Efficiency\",\n \"type\": \"report\"\n }\n]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "CRM",
|
"label": "CRM",
|
||||||
"modified": "2020-05-28 13:33:52.906750",
|
"modified": "2020-08-11 18:55:18.238900",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "CRM",
|
"name": "CRM",
|
||||||
|
@ -27,9 +27,6 @@ class Lead(SellingController):
|
|||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
self.update_links()
|
self.update_links()
|
||||||
# after the address and contact are created, flush the field values
|
|
||||||
# to avoid inconsistent reporting in case the documents are changed
|
|
||||||
self.flush_address_and_contact_fields()
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.set_lead_name()
|
self.set_lead_name()
|
||||||
@ -210,14 +207,6 @@ class Lead(SellingController):
|
|||||||
})
|
})
|
||||||
self.contact_doc.save()
|
self.contact_doc.save()
|
||||||
|
|
||||||
def flush_address_and_contact_fields(self):
|
|
||||||
fields = ['address_type', 'address_line1', 'address_line2', 'address_title',
|
|
||||||
'city', 'county', 'country', 'fax', 'pincode', 'state']
|
|
||||||
|
|
||||||
for field in fields:
|
|
||||||
self.set(field, None)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_customer(source_name, target_doc=None):
|
def make_customer(source_name, target_doc=None):
|
||||||
return _make_customer(source_name, target_doc)
|
return _make_customer(source_name, target_doc)
|
||||||
@ -376,3 +365,8 @@ def get_lead_with_phone_number(number):
|
|||||||
lead = leads[0].name if leads else None
|
lead = leads[0].name if leads else None
|
||||||
|
|
||||||
return lead
|
return lead
|
||||||
|
|
||||||
|
def daily_open_lead():
|
||||||
|
leads = frappe.get_all("Lead", filters = [["contact_date", "Between", [nowdate(), nowdate()]]])
|
||||||
|
for lead in leads:
|
||||||
|
frappe.db.set_value("Lead", lead.name, "status", "Open")
|
@ -24,7 +24,7 @@
|
|||||||
"converted_by",
|
"converted_by",
|
||||||
"sales_stage",
|
"sales_stage",
|
||||||
"order_lost_reason",
|
"order_lost_reason",
|
||||||
"mins_to_first_response",
|
"first_response_time",
|
||||||
"expected_closing",
|
"expected_closing",
|
||||||
"next_contact",
|
"next_contact",
|
||||||
"contact_by",
|
"contact_by",
|
||||||
@ -152,13 +152,6 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"bold": 1,
|
|
||||||
"fieldname": "mins_to_first_response",
|
|
||||||
"fieldtype": "Float",
|
|
||||||
"label": "Mins to first response",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "expected_closing",
|
"fieldname": "expected_closing",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
@ -419,12 +412,19 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Converted By",
|
"label": "Converted By",
|
||||||
"options": "User"
|
"options": "User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bold": 1,
|
||||||
|
"fieldname": "first_response_time",
|
||||||
|
"fieldtype": "Duration",
|
||||||
|
"label": "First Response Time",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-info-sign",
|
"icon": "fa fa-info-sign",
|
||||||
"idx": 195,
|
"idx": 195,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-08-11 17:34:35.066961",
|
"modified": "2020-08-12 17:34:35.066961",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Opportunity",
|
"name": "Opportunity",
|
||||||
|
@ -267,6 +267,9 @@ def make_quotation(source_name, target_doc=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_request_for_quotation(source_name, target_doc=None):
|
def make_request_for_quotation(source_name, target_doc=None):
|
||||||
|
def update_item(obj, target, source_parent):
|
||||||
|
target.conversion_factor = 1.0
|
||||||
|
|
||||||
doclist = get_mapped_doc("Opportunity", source_name, {
|
doclist = get_mapped_doc("Opportunity", source_name, {
|
||||||
"Opportunity": {
|
"Opportunity": {
|
||||||
"doctype": "Request for Quotation"
|
"doctype": "Request for Quotation"
|
||||||
@ -277,7 +280,8 @@ def make_request_for_quotation(source_name, target_doc=None):
|
|||||||
["name", "opportunity_item"],
|
["name", "opportunity_item"],
|
||||||
["parent", "opportunity"],
|
["parent", "opportunity"],
|
||||||
["uom", "uom"]
|
["uom", "uom"]
|
||||||
]
|
],
|
||||||
|
"postprocess": update_item
|
||||||
}
|
}
|
||||||
}, target_doc)
|
}, target_doc)
|
||||||
|
|
||||||
|
@ -82,7 +82,8 @@ def make_opportunity(**args):
|
|||||||
if args.with_items:
|
if args.with_items:
|
||||||
opp_doc.append('items', {
|
opp_doc.append('items', {
|
||||||
"item_code": args.item_code or "_Test Item",
|
"item_code": args.item_code or "_Test Item",
|
||||||
"qty": args.qty or 1
|
"qty": args.qty or 1,
|
||||||
|
"uom": "_Test UOM"
|
||||||
})
|
})
|
||||||
|
|
||||||
opp_doc.insert()
|
opp_doc.insert()
|
||||||
|
@ -1,33 +1,44 @@
|
|||||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
frappe.query_reports["Minutes to First Response for Opportunity"] = {
|
frappe.query_reports["First Response Time for Opportunity"] = {
|
||||||
"filters": [
|
"filters": [
|
||||||
{
|
{
|
||||||
"fieldname": "from_date",
|
"fieldname": "from_date",
|
||||||
"label": __("From Date"),
|
"label": __("From Date"),
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
'reqd': 1,
|
"reqd": 1,
|
||||||
"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
|
"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "to_date",
|
"fieldname": "to_date",
|
||||||
"label": __("To Date"),
|
"label": __("To Date"),
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
'reqd': 1,
|
"reqd": 1,
|
||||||
"default": frappe.datetime.nowdate()
|
"default": frappe.datetime.nowdate()
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
get_chart_data: function (columns, result) {
|
get_chart_data: function (_columns, result) {
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
labels: result.map(d => d[0]),
|
labels: result.map(d => d[0]),
|
||||||
datasets: [{
|
datasets: [{
|
||||||
name: 'Mins to first response',
|
name: "First Response Time",
|
||||||
values: result.map(d => d[1])
|
values: result.map(d => d[1])
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
type: 'line',
|
type: "line",
|
||||||
|
tooltipOptions: {
|
||||||
|
formatTooltipY: d => {
|
||||||
|
let duration_options = {
|
||||||
|
hide_days: 0,
|
||||||
|
hide_seconds: 0
|
||||||
|
};
|
||||||
|
value = frappe.utils.get_formatted_duration(d, duration_options);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"creation": "2020-08-10 18:34:19.083872",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"letter_head": "Test 2",
|
||||||
|
"modified": "2020-08-10 18:34:19.083872",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "CRM",
|
||||||
|
"name": "First Response Time for Opportunity",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Opportunity",
|
||||||
|
"report_name": "First Response Time for Opportunity",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Sales User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Sales Manager"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
columns = [
|
||||||
|
{
|
||||||
|
'fieldname': 'creation_date',
|
||||||
|
'label': 'Date',
|
||||||
|
'fieldtype': 'Date',
|
||||||
|
'width': 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'first_response_time',
|
||||||
|
'fieldtype': 'Duration',
|
||||||
|
'label': 'First Response Time',
|
||||||
|
'width': 300
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
data = frappe.db.sql('''
|
||||||
|
SELECT
|
||||||
|
date(creation) as creation_date,
|
||||||
|
avg(first_response_time) as avg_response_time
|
||||||
|
FROM tabOpportunity
|
||||||
|
WHERE
|
||||||
|
date(creation) between %s and %s
|
||||||
|
and first_response_time > 0
|
||||||
|
GROUP BY creation_date
|
||||||
|
ORDER BY creation_date desc
|
||||||
|
''', (filters.from_date, filters.to_date))
|
||||||
|
|
||||||
|
return columns, data
|
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"add_total_row": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"creation": "2016-06-17 11:28:25.867258",
|
|
||||||
"disabled": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Report",
|
|
||||||
"idx": 2,
|
|
||||||
"is_standard": "Yes",
|
|
||||||
"modified": "2017-02-24 20:06:08.801109",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "CRM",
|
|
||||||
"name": "Minutes to First Response for Opportunity",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"ref_doctype": "Opportunity",
|
|
||||||
"report_name": "Minutes to First Response for Opportunity",
|
|
||||||
"report_type": "Script Report",
|
|
||||||
"roles": [
|
|
||||||
{
|
|
||||||
"role": "Sales User"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "Sales Manager"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||||
# For license information, please see license.txt
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
|
||||||
|
|
||||||
def execute(filters=None):
|
|
||||||
columns = [
|
|
||||||
{
|
|
||||||
'fieldname': 'creation_date',
|
|
||||||
'label': 'Date',
|
|
||||||
'fieldtype': 'Date'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'fieldname': 'mins',
|
|
||||||
'fieldtype': 'Float',
|
|
||||||
'label': 'Mins to First Response'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
data = frappe.db.sql('''select date(creation) as creation_date,
|
|
||||||
avg(mins_to_first_response) as mins
|
|
||||||
from tabOpportunity
|
|
||||||
where date(creation) between %s and %s
|
|
||||||
and mins_to_first_response > 0
|
|
||||||
group by creation_date order by creation_date desc''', (filters.from_date, filters.to_date))
|
|
||||||
|
|
||||||
return columns, data
|
|
@ -13,7 +13,7 @@
|
|||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "question",
|
"fieldname": "question",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Text Editor",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Question",
|
"label": "Question",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
@ -34,7 +34,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-05-30 18:39:21.880974",
|
"modified": "2020-09-24 18:39:21.880974",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Question",
|
"name": "Question",
|
||||||
@ -77,4 +77,4 @@
|
|||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC"
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,14 @@
|
|||||||
{
|
{
|
||||||
"fetch_from": "question_link.question",
|
"fetch_from": "question_link.question",
|
||||||
"fieldname": "question",
|
"fieldname": "question",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Text Editor",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Question",
|
"label": "Question",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-06-12 12:24:02.312577",
|
"modified": "2020-09-24 12:24:02.312577",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Quiz Question",
|
"name": "Quiz Question",
|
||||||
@ -37,4 +37,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Student', {
|
frappe.ui.form.on('Student', {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("guardian", "guardian_name", "guardian_name");
|
frm.add_fetch('guardian', 'guardian_name', 'guardian_name');
|
||||||
frm.add_fetch("student", "title", "full_name");
|
frm.add_fetch('student', 'title', 'full_name');
|
||||||
frm.add_fetch("student", "gender", "gender");
|
frm.add_fetch('student', 'gender', 'gender');
|
||||||
frm.add_fetch("student", "date_of_birth", "date_of_birth");
|
frm.add_fetch('student', 'date_of_birth', 'date_of_birth');
|
||||||
|
|
||||||
frm.set_query("student", "siblings", function(doc, cdt, cdn) {
|
frm.set_query('student', 'siblings', function(doc) {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
'filters': {
|
||||||
"name": ["!=", doc.name]
|
'name': ['!=', doc.name]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -25,6 +25,12 @@ frappe.ui.form.on('Student', {
|
|||||||
{party_type:'Student', party:frm.doc.name});
|
{party_type:'Student', party:frm.doc.name});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frappe.db.get_value('Education Settings', {name: 'Education Settings'}, 'user_creation_skip', (r) => {
|
||||||
|
if (cint(r.user_creation_skip) !== 1) {
|
||||||
|
frm.set_df_property('student_email_id', 'reqd', 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -102,7 +102,6 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"label": "Student Email Address",
|
"label": "Student Email Address",
|
||||||
"reqd": 1,
|
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -255,7 +254,7 @@
|
|||||||
],
|
],
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-23 18:14:06.366442",
|
"modified": "2020-09-07 19:28:08.914568",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Student",
|
"name": "Student",
|
||||||
|
@ -7,7 +7,7 @@ frappe.ui.form.on("Student Applicant", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if(frm.doc.application_status== "Applied" && frm.doc.docstatus== 1 ) {
|
if (frm.doc.application_status==="Applied" && frm.doc.docstatus===1 ) {
|
||||||
frm.add_custom_button(__("Approve"), function() {
|
frm.add_custom_button(__("Approve"), function() {
|
||||||
frm.set_value("application_status", "Approved");
|
frm.set_value("application_status", "Approved");
|
||||||
frm.save_or_update();
|
frm.save_or_update();
|
||||||
@ -20,10 +20,11 @@ frappe.ui.form.on("Student Applicant", {
|
|||||||
}, 'Actions');
|
}, 'Actions');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(frm.doc.application_status== "Approved" && frm.doc.docstatus== 1 ) {
|
if (frm.doc.application_status === "Approved" && frm.doc.docstatus === 1) {
|
||||||
frm.add_custom_button(__("Enroll"), function() {
|
frm.add_custom_button(__("Enroll"), function() {
|
||||||
frm.events.enroll(frm)
|
frm.events.enroll(frm)
|
||||||
}).addClass("btn-primary");
|
}).addClass("btn-primary");
|
||||||
|
|
||||||
frm.add_custom_button(__("Reject"), function() {
|
frm.add_custom_button(__("Reject"), function() {
|
||||||
frm.set_value("application_status", "Rejected");
|
frm.set_value("application_status", "Rejected");
|
||||||
frm.save_or_update();
|
frm.save_or_update();
|
||||||
@ -35,7 +36,13 @@ frappe.ui.form.on("Student Applicant", {
|
|||||||
frappe.hide_msgprint(true);
|
frappe.hide_msgprint(true);
|
||||||
frappe.show_progress(__("Enrolling student"), data.progress[0],data.progress[1]);
|
frappe.show_progress(__("Enrolling student"), data.progress[0],data.progress[1]);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
frappe.db.get_value("Education Settings", {name: "Education Settings"}, "user_creation_skip", (r) => {
|
||||||
|
if (cint(r.user_creation_skip) !== 1) {
|
||||||
|
frm.set_df_property("student_email_id", "reqd", 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
enroll: function(frm) {
|
enroll: function(frm) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Marketplace",
|
||||||
|
"links": "[\n {\n \"description\": \"Woocommerce marketplace settings\",\n \"label\": \"Woocommerce Settings\",\n \"name\": \"Woocommerce Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Amazon MWS settings\",\n \"label\": \"Amazon MWS Settings\",\n \"name\": \"Amazon MWS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Shopify settings\",\n \"label\": \"Shopify Settings\",\n \"name\": \"Shopify Settings\",\n \"type\": \"doctype\"\n }\n]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Payments",
|
||||||
|
"links": "[\n {\n \"description\": \"GoCardless payment gateway settings\",\n \"label\": \"GoCardless Settings\",\n \"name\": \"GoCardless Settings\",\n \"type\": \"doctype\"\n }\n]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Settings",
|
||||||
|
"links": "[\n {\n \"description\": \"Plaid settings\",\n \"label\": \"Plaid Settings\",\n \"name\": \"Plaid Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Exotel settings\",\n \"label\": \"Exotel Settings\",\n \"name\": \"Exotel Settings\",\n \"type\": \"doctype\"\n }\n]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"category": "Modules",
|
||||||
|
"charts": [],
|
||||||
|
"creation": "2020-08-20 19:30:48.138801",
|
||||||
|
"developer_mode_only": 0,
|
||||||
|
"disable_user_customization": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Desk Page",
|
||||||
|
"extends": "Integrations",
|
||||||
|
"extends_another_page": 1,
|
||||||
|
"hide_custom": 1,
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": 1,
|
||||||
|
"label": "ERPNext Integrations",
|
||||||
|
"modified": "2020-08-23 16:30:51.494655",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "ERPNext Integrations",
|
||||||
|
"name": "ERPNext Integrations",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"pin_to_bottom": 0,
|
||||||
|
"pin_to_top": 0,
|
||||||
|
"shortcuts": []
|
||||||
|
}
|
@ -2,81 +2,90 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2018, 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
|
import plaid
|
||||||
from frappe import _
|
import requests
|
||||||
from frappe.utils.password import get_decrypted_password
|
from plaid.errors import APIError, ItemError, InvalidRequestError
|
||||||
from plaid import Client
|
|
||||||
from plaid.errors import APIError, ItemError
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import requests
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
class PlaidConnector():
|
class PlaidConnector():
|
||||||
def __init__(self, access_token=None):
|
def __init__(self, access_token=None):
|
||||||
|
|
||||||
plaid_settings = frappe.get_single("Plaid Settings")
|
|
||||||
|
|
||||||
self.config = {
|
|
||||||
"plaid_client_id": plaid_settings.plaid_client_id,
|
|
||||||
"plaid_secret": get_decrypted_password("Plaid Settings", "Plaid Settings", 'plaid_secret'),
|
|
||||||
"plaid_public_key": plaid_settings.plaid_public_key,
|
|
||||||
"plaid_env": plaid_settings.plaid_env
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client = Client(client_id=self.config.get("plaid_client_id"),
|
|
||||||
secret=self.config.get("plaid_secret"),
|
|
||||||
public_key=self.config.get("plaid_public_key"),
|
|
||||||
environment=self.config.get("plaid_env")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.access_token = access_token
|
self.access_token = access_token
|
||||||
|
self.settings = frappe.get_single("Plaid Settings")
|
||||||
|
self.products = ["auth", "transactions"]
|
||||||
|
self.client_name = frappe.local.site
|
||||||
|
self.client = plaid.Client(
|
||||||
|
client_id=self.settings.plaid_client_id,
|
||||||
|
secret=self.settings.get_password("plaid_secret"),
|
||||||
|
environment=self.settings.plaid_env,
|
||||||
|
api_version="2019-05-29"
|
||||||
|
)
|
||||||
|
|
||||||
def get_access_token(self, public_token):
|
def get_access_token(self, public_token):
|
||||||
if public_token is None:
|
if public_token is None:
|
||||||
frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
|
frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
|
||||||
|
|
||||||
response = self.client.Item.public_token.exchange(public_token)
|
response = self.client.Item.public_token.exchange(public_token)
|
||||||
access_token = response['access_token']
|
access_token = response["access_token"]
|
||||||
|
|
||||||
return access_token
|
return access_token
|
||||||
|
|
||||||
|
def get_link_token(self):
|
||||||
|
token_request = {
|
||||||
|
"client_name": self.client_name,
|
||||||
|
"client_id": self.settings.plaid_client_id,
|
||||||
|
"secret": self.settings.plaid_secret,
|
||||||
|
"products": self.products,
|
||||||
|
# only allow Plaid-supported languages and countries (LAST: Sep-19-2020)
|
||||||
|
"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
|
||||||
|
"country_codes": ["US", "CA", "FR", "IE", "NL", "ES", "GB"],
|
||||||
|
"user": {
|
||||||
|
"client_user_id": frappe.generate_hash(frappe.session.user, length=32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = self.client.LinkToken.create(token_request)
|
||||||
|
except InvalidRequestError:
|
||||||
|
frappe.log_error(frappe.get_traceback(), _("Plaid invalid request error"))
|
||||||
|
frappe.msgprint(_("Please check your Plaid client ID and secret values"))
|
||||||
|
except APIError as e:
|
||||||
|
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
|
||||||
|
frappe.throw(_(str(e)), title=_("Authentication Failed"))
|
||||||
|
else:
|
||||||
|
return response["link_token"]
|
||||||
|
|
||||||
def auth(self):
|
def auth(self):
|
||||||
try:
|
try:
|
||||||
self.client.Auth.get(self.access_token)
|
self.client.Auth.get(self.access_token)
|
||||||
print("Authentication successful.....")
|
|
||||||
except ItemError as e:
|
except ItemError as e:
|
||||||
if e.code == 'ITEM_LOGIN_REQUIRED':
|
if e.code == "ITEM_LOGIN_REQUIRED":
|
||||||
pass
|
|
||||||
else:
|
|
||||||
pass
|
pass
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
if e.code == 'PLANNED_MAINTENANCE':
|
if e.code == "PLANNED_MAINTENANCE":
|
||||||
pass
|
|
||||||
else:
|
|
||||||
pass
|
pass
|
||||||
except requests.Timeout:
|
except requests.Timeout:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
|
||||||
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
|
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
|
||||||
frappe.msgprint({"title": _("Authentication Failed"), "message":e, "raise_exception":1, "indicator":'red'})
|
frappe.throw(_(str(e)), title=_("Authentication Failed"))
|
||||||
|
|
||||||
def get_transactions(self, start_date, end_date, account_id=None):
|
def get_transactions(self, start_date, end_date, account_id=None):
|
||||||
|
self.auth()
|
||||||
|
kwargs = dict(
|
||||||
|
access_token=self.access_token,
|
||||||
|
start_date=start_date,
|
||||||
|
end_date=end_date
|
||||||
|
)
|
||||||
|
if account_id:
|
||||||
|
kwargs.update(dict(account_ids=[account_id]))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.auth()
|
response = self.client.Transactions.get(**kwargs)
|
||||||
if account_id:
|
transactions = response["transactions"]
|
||||||
account_ids = [account_id]
|
while len(transactions) < response["total_transactions"]:
|
||||||
|
|
||||||
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, account_ids=account_ids)
|
|
||||||
|
|
||||||
else:
|
|
||||||
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date)
|
|
||||||
|
|
||||||
transactions = response['transactions']
|
|
||||||
|
|
||||||
while len(transactions) < response['total_transactions']:
|
|
||||||
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
|
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
|
||||||
transactions.extend(response['transactions'])
|
transactions.extend(response["transactions"])
|
||||||
return transactions
|
return transactions
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
frappe.provide("erpnext.integrations");
|
frappe.provide("erpnext.integrations");
|
||||||
|
|
||||||
frappe.ui.form.on('Plaid Settings', {
|
frappe.ui.form.on('Plaid Settings', {
|
||||||
enabled: function(frm) {
|
enabled: function (frm) {
|
||||||
frm.toggle_reqd('plaid_client_id', frm.doc.enabled);
|
frm.toggle_reqd('plaid_client_id', frm.doc.enabled);
|
||||||
frm.toggle_reqd('plaid_secret', frm.doc.enabled);
|
frm.toggle_reqd('plaid_secret', frm.doc.enabled);
|
||||||
frm.toggle_reqd('plaid_public_key', frm.doc.enabled);
|
|
||||||
frm.toggle_reqd('plaid_env', frm.doc.enabled);
|
frm.toggle_reqd('plaid_env', frm.doc.enabled);
|
||||||
},
|
},
|
||||||
refresh: function(frm) {
|
|
||||||
if(frm.doc.enabled) {
|
refresh: function (frm) {
|
||||||
|
if (frm.doc.enabled) {
|
||||||
frm.add_custom_button('Link a new bank account', () => {
|
frm.add_custom_button('Link a new bank account', () => {
|
||||||
new erpnext.integrations.plaidLink(frm);
|
new erpnext.integrations.plaidLink(frm);
|
||||||
});
|
});
|
||||||
@ -22,17 +22,16 @@ frappe.ui.form.on('Plaid Settings', {
|
|||||||
erpnext.integrations.plaidLink = class plaidLink {
|
erpnext.integrations.plaidLink = class plaidLink {
|
||||||
constructor(parent) {
|
constructor(parent) {
|
||||||
this.frm = parent;
|
this.frm = parent;
|
||||||
this.product = ["transactions", "auth"];
|
|
||||||
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
|
||||||
this.init_config();
|
this.init_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
init_config() {
|
async init_config() {
|
||||||
const me = this;
|
this.product = ["auth", "transactions"];
|
||||||
me.plaid_env = me.frm.doc.plaid_env;
|
this.plaid_env = this.frm.doc.plaid_env;
|
||||||
me.plaid_public_key = me.frm.doc.plaid_public_key;
|
this.client_name = frappe.boot.sitename;
|
||||||
me.client_name = frappe.boot.sitename;
|
this.token = await this.frm.call("get_link_token").then(resp => resp.message);
|
||||||
me.init_plaid();
|
this.init_plaid();
|
||||||
}
|
}
|
||||||
|
|
||||||
init_plaid() {
|
init_plaid() {
|
||||||
@ -69,17 +68,17 @@ erpnext.integrations.plaidLink = class plaidLink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onScriptLoaded(me) {
|
onScriptLoaded(me) {
|
||||||
me.linkHandler = window.Plaid.create({
|
me.linkHandler = Plaid.create({
|
||||||
clientName: me.client_name,
|
clientName: me.client_name,
|
||||||
|
product: me.product,
|
||||||
env: me.plaid_env,
|
env: me.plaid_env,
|
||||||
key: me.plaid_public_key,
|
token: me.token,
|
||||||
onSuccess: me.plaid_success,
|
onSuccess: me.plaid_success
|
||||||
product: me.product
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onScriptError(error) {
|
onScriptError(error) {
|
||||||
frappe.msgprint('There was an issue loading the link-initialize.js script');
|
frappe.msgprint("There was an issue connecting to Plaid's authentication server");
|
||||||
frappe.msgprint(error);
|
frappe.msgprint(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,21 +86,25 @@ erpnext.integrations.plaidLink = class plaidLink {
|
|||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
frappe.prompt({
|
frappe.prompt({
|
||||||
fieldtype:"Link",
|
fieldtype: "Link",
|
||||||
options: "Company",
|
options: "Company",
|
||||||
label:__("Company"),
|
label: __("Company"),
|
||||||
fieldname:"company",
|
fieldname: "company",
|
||||||
reqd:1
|
reqd: 1
|
||||||
}, (data) => {
|
}, (data) => {
|
||||||
me.company = data.company;
|
me.company = data.company;
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {token: token, response: response})
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {
|
||||||
.then((result) => {
|
token: token,
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {response: response,
|
response: response
|
||||||
bank: result, company: me.company});
|
}).then((result) => {
|
||||||
})
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {
|
||||||
.then(() => {
|
response: response,
|
||||||
frappe.show_alert({message:__("Bank accounts added"), indicator:'green'});
|
bank: result,
|
||||||
|
company: me.company
|
||||||
});
|
});
|
||||||
|
}).then(() => {
|
||||||
|
frappe.show_alert({ message: __("Bank accounts added"), indicator: 'green' });
|
||||||
|
});
|
||||||
}, __("Select a company"), __("Continue"));
|
}, __("Select a company"), __("Continue"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
|
||||||
"creation": "2018-10-25 10:02:48.656165",
|
"creation": "2018-10-25 10:02:48.656165",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@ -12,7 +11,6 @@
|
|||||||
"plaid_client_id",
|
"plaid_client_id",
|
||||||
"plaid_secret",
|
"plaid_secret",
|
||||||
"column_break_7",
|
"column_break_7",
|
||||||
"plaid_public_key",
|
|
||||||
"plaid_env"
|
"plaid_env"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -41,12 +39,6 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Plaid Secret"
|
"label": "Plaid Secret"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "plaid_public_key",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Plaid Public Key"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "plaid_env",
|
"fieldname": "plaid_env",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@ -69,8 +61,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"modified": "2020-09-12 02:31:44.542385",
|
||||||
"modified": "2020-02-07 15:21:11.616231",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "ERPNext Integrations",
|
"module": "ERPNext Integrations",
|
||||||
"name": "Plaid Settings",
|
"name": "Plaid Settings",
|
||||||
|
@ -2,30 +2,36 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2018, 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
|
|
||||||
import frappe
|
|
||||||
import json
|
import json
|
||||||
from frappe import _
|
|
||||||
from frappe.model.document import Document
|
import frappe
|
||||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||||
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector
|
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector
|
||||||
from frappe.utils import getdate, formatdate, today, add_months
|
from frappe import _
|
||||||
from frappe.desk.doctype.tag.tag import add_tag
|
from frappe.desk.doctype.tag.tag import add_tag
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import add_months, formatdate, getdate, today
|
||||||
|
|
||||||
|
|
||||||
class PlaidSettings(Document):
|
class PlaidSettings(Document):
|
||||||
pass
|
@staticmethod
|
||||||
|
def get_link_token():
|
||||||
|
plaid = PlaidConnector()
|
||||||
|
return plaid.get_link_token()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def plaid_configuration():
|
def get_plaid_configuration():
|
||||||
if frappe.db.get_single_value("Plaid Settings", "enabled"):
|
if frappe.db.get_single_value("Plaid Settings", "enabled"):
|
||||||
plaid_settings = frappe.get_single("Plaid Settings")
|
plaid_settings = frappe.get_single("Plaid Settings")
|
||||||
return {
|
return {
|
||||||
"plaid_public_key": plaid_settings.plaid_public_key,
|
|
||||||
"plaid_env": plaid_settings.plaid_env,
|
"plaid_env": plaid_settings.plaid_env,
|
||||||
|
"link_token": plaid_settings.get_link_token(),
|
||||||
"client_name": frappe.local.site
|
"client_name": frappe.local.site
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
return "disabled"
|
return "disabled"
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_institution(token, response):
|
def add_institution(token, response):
|
||||||
@ -33,6 +39,7 @@ def add_institution(token, response):
|
|||||||
|
|
||||||
plaid = PlaidConnector()
|
plaid = PlaidConnector()
|
||||||
access_token = plaid.get_access_token(token)
|
access_token = plaid.get_access_token(token)
|
||||||
|
bank = None
|
||||||
|
|
||||||
if not frappe.db.exists("Bank", response["institution"]["name"]):
|
if not frappe.db.exists("Bank", response["institution"]["name"]):
|
||||||
try:
|
try:
|
||||||
@ -44,7 +51,6 @@ def add_institution(token, response):
|
|||||||
bank.insert()
|
bank.insert()
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.throw(frappe.get_traceback())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
||||||
bank.plaid_access_token = access_token
|
bank.plaid_access_token = access_token
|
||||||
@ -52,6 +58,7 @@ def add_institution(token, response):
|
|||||||
|
|
||||||
return bank
|
return bank
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_bank_accounts(response, bank, company):
|
def add_bank_accounts(response, bank, company):
|
||||||
try:
|
try:
|
||||||
@ -92,9 +99,8 @@ def add_bank_accounts(response, bank, company):
|
|||||||
new_account.insert()
|
new_account.insert()
|
||||||
|
|
||||||
result.append(new_account.name)
|
result.append(new_account.name)
|
||||||
|
|
||||||
except frappe.UniqueValidationError:
|
except frappe.UniqueValidationError:
|
||||||
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(new_account.account_name))
|
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.throw(frappe.get_traceback())
|
||||||
|
|
||||||
@ -103,6 +109,7 @@ def add_bank_accounts(response, bank, company):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def add_account_type(account_type):
|
def add_account_type(account_type):
|
||||||
try:
|
try:
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
@ -122,10 +129,11 @@ def add_account_subtype(account_subtype):
|
|||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.throw(frappe.get_traceback())
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def sync_transactions(bank, bank_account):
|
def sync_transactions(bank, bank_account):
|
||||||
'''Sync transactions based on the last integration date as the start date, after the sync is completed
|
"""Sync transactions based on the last integration date as the start date, after sync is completed
|
||||||
add the transaction date of the oldest transaction as the last integration date'''
|
add the transaction date of the oldest transaction as the last integration date."""
|
||||||
last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
|
last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
|
||||||
if last_transaction_date:
|
if last_transaction_date:
|
||||||
start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
|
start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
|
||||||
@ -147,10 +155,10 @@ def sync_transactions(bank, bank_account):
|
|||||||
len(result), bank_account, start_date, end_date))
|
len(result), bank_account, start_date, end_date))
|
||||||
|
|
||||||
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
|
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
||||||
|
|
||||||
|
|
||||||
def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
||||||
access_token = None
|
access_token = None
|
||||||
|
|
||||||
@ -168,6 +176,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
|||||||
|
|
||||||
return transactions
|
return transactions
|
||||||
|
|
||||||
|
|
||||||
def new_bank_transaction(transaction):
|
def new_bank_transaction(transaction):
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
@ -182,8 +191,8 @@ def new_bank_transaction(transaction):
|
|||||||
|
|
||||||
status = "Pending" if transaction["pending"] == "True" else "Settled"
|
status = "Pending" if transaction["pending"] == "True" else "Settled"
|
||||||
|
|
||||||
|
tags = []
|
||||||
try:
|
try:
|
||||||
tags = []
|
|
||||||
tags += transaction["category"]
|
tags += transaction["category"]
|
||||||
tags += ["Plaid Cat. {}".format(transaction["category_id"])]
|
tags += ["Plaid Cat. {}".format(transaction["category_id"])]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -216,6 +225,7 @@ def new_bank_transaction(transaction):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def automatic_synchronization():
|
def automatic_synchronization():
|
||||||
settings = frappe.get_doc("Plaid Settings", "Plaid Settings")
|
settings = frappe.get_doc("Plaid Settings", "Plaid Settings")
|
||||||
|
|
||||||
@ -223,4 +233,8 @@ def automatic_synchronization():
|
|||||||
plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"])
|
plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"])
|
||||||
|
|
||||||
for plaid_account in plaid_accounts:
|
for plaid_account in plaid_accounts:
|
||||||
frappe.enqueue("erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions", bank=plaid_account.bank, bank_account=plaid_account.name)
|
frappe.enqueue(
|
||||||
|
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions",
|
||||||
|
bank=plaid_account.bank,
|
||||||
|
bank_account=plaid_account.name
|
||||||
|
)
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
import frappe
|
|
||||||
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import plaid_configuration, add_account_type, add_account_subtype, new_bank_transaction, add_bank_accounts
|
|
||||||
import json
|
import json
|
||||||
from frappe.utils.response import json_handler
|
import unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||||
|
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import (
|
||||||
|
add_account_subtype, add_account_type, add_bank_accounts,
|
||||||
|
new_bank_transaction, get_plaid_configuration)
|
||||||
|
from frappe.utils.response import json_handler
|
||||||
|
|
||||||
|
|
||||||
class TestPlaidSettings(unittest.TestCase):
|
class TestPlaidSettings(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -31,7 +34,7 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
|
|
||||||
def test_plaid_disabled(self):
|
def test_plaid_disabled(self):
|
||||||
frappe.db.set_value("Plaid Settings", None, "enabled", 0)
|
frappe.db.set_value("Plaid Settings", None, "enabled", 0)
|
||||||
self.assertTrue(plaid_configuration() == "disabled")
|
self.assertTrue(get_plaid_configuration() == "disabled")
|
||||||
|
|
||||||
def test_add_account_type(self):
|
def test_add_account_type(self):
|
||||||
add_account_type("brokerage")
|
add_account_type("brokerage")
|
||||||
@ -64,7 +67,7 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
'mask': '0000',
|
'mask': '0000',
|
||||||
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||||
'name': 'Plaid Checking'
|
'name': 'Plaid Checking'
|
||||||
}],
|
}],
|
||||||
'institution': {
|
'institution': {
|
||||||
'institution_id': 'ins_6',
|
'institution_id': 'ins_6',
|
||||||
'name': 'Citi'
|
'name': 'Citi'
|
||||||
@ -100,7 +103,7 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
'mask': '0000',
|
'mask': '0000',
|
||||||
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
|
||||||
'name': 'Plaid Checking'
|
'name': 'Plaid Checking'
|
||||||
}],
|
}],
|
||||||
'institution': {
|
'institution': {
|
||||||
'institution_id': 'ins_6',
|
'institution_id': 'ins_6',
|
||||||
'name': 'Citi'
|
'name': 'Citi'
|
||||||
@ -152,4 +155,4 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
|
|
||||||
new_bank_transaction(transactions)
|
new_bank_transaction(transactions)
|
||||||
|
|
||||||
self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)
|
self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)
|
||||||
|
@ -258,8 +258,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"modified": "2020-05-28 12:32:11.384757",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "umair@erpnext.com",
|
"modified_by": "Administrator",
|
||||||
"module": "ERPNext Integrations",
|
"module": "ERPNext Integrations",
|
||||||
"name": "Shopify Settings",
|
"name": "Shopify Settings",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
@ -117,12 +117,12 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-05-08 13:45:57.226530",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Healthcare Schedule Time Slot",
|
"name": "Healthcare Schedule Time Slot",
|
||||||
"name_case": "",
|
"name_case": "",
|
||||||
"owner": "rmehta@gmail.com",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
@ -34,10 +34,10 @@ frappe.ui.form.on('Lab Test', {
|
|||||||
if (frm.doc.docstatus === 1 && frm.doc.status !== 'Approved' && frm.doc.status !== 'Rejected') {
|
if (frm.doc.docstatus === 1 && frm.doc.status !== 'Approved' && frm.doc.status !== 'Rejected') {
|
||||||
frm.add_custom_button(__('Approve'), function () {
|
frm.add_custom_button(__('Approve'), function () {
|
||||||
status_update(1, frm);
|
status_update(1, frm);
|
||||||
});
|
}, __('Actions'));
|
||||||
frm.add_custom_button(__('Reject'), function () {
|
frm.add_custom_button(__('Reject'), function () {
|
||||||
status_update(0, frm);
|
status_update(0, frm);
|
||||||
});
|
}, __('Actions'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,23 +179,6 @@ var show_lab_tests = function (frm, lab_test_list) {
|
|||||||
d.show();
|
d.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
cur_frm.cscript.custom_before_submit = function (doc) {
|
|
||||||
if (doc.normal_test_items) {
|
|
||||||
for (let result in doc.normal_test_items) {
|
|
||||||
if (!doc.normal_test_items[result].result_value && !doc.normal_test_items[result].allow_blank && doc.normal_test_items[result].require_result_value) {
|
|
||||||
frappe.throw(__('Please input all required result values'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (doc.descriptive_test_items) {
|
|
||||||
for (let result in doc.descriptive_test_items) {
|
|
||||||
if (!doc.descriptive_test_items[result].result_value && !doc.descriptive_test_items[result].allow_blank && doc.descriptive_test_items[result].require_result_value) {
|
|
||||||
frappe.throw(__('Please input all required result values'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var make_dialog = function (frm, emailed, printed) {
|
var make_dialog = function (frm, emailed, printed) {
|
||||||
var number = frm.doc.mobile;
|
var number = frm.doc.mobile;
|
||||||
|
|
||||||
@ -203,7 +186,7 @@ var make_dialog = function (frm, emailed, printed) {
|
|||||||
title: 'Send SMS',
|
title: 'Send SMS',
|
||||||
width: 400,
|
width: 400,
|
||||||
fields: [
|
fields: [
|
||||||
{ fieldname: 'sms_type', fieldtype: 'Select', label: 'Type', options: ['Emailed', 'Printed'] },
|
{ fieldname: 'result_format', fieldtype: 'Select', label: 'Result Format', options: ['Emailed', 'Printed'] },
|
||||||
{ fieldname: 'number', fieldtype: 'Data', label: 'Mobile Number', reqd: 1 },
|
{ fieldname: 'number', fieldtype: 'Data', label: 'Mobile Number', reqd: 1 },
|
||||||
{ fieldname: 'message', fieldtype: 'Small Text', label: 'Message', reqd: 1 }
|
{ fieldname: 'message', fieldtype: 'Small Text', label: 'Message', reqd: 1 }
|
||||||
],
|
],
|
||||||
@ -217,22 +200,22 @@ var make_dialog = function (frm, emailed, printed) {
|
|||||||
dialog.hide();
|
dialog.hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (frm.doc.report_preference == 'Print') {
|
if (frm.doc.report_preference === 'Print') {
|
||||||
dialog.set_values({
|
dialog.set_values({
|
||||||
'sms_type': 'Printed',
|
'result_format': 'Printed',
|
||||||
'number': number,
|
'number': number,
|
||||||
'message': printed
|
'message': printed
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
dialog.set_values({
|
dialog.set_values({
|
||||||
'sms_type': 'Emailed',
|
'result_format': 'Emailed',
|
||||||
'number': number,
|
'number': number,
|
||||||
'message': emailed
|
'message': emailed
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var fd = dialog.fields_dict;
|
var fd = dialog.fields_dict;
|
||||||
$(fd.sms_type.input).change(function () {
|
$(fd.result_format.input).change(function () {
|
||||||
if (dialog.get_value('sms_type') == 'Emailed') {
|
if (dialog.get_value('result_format') === 'Emailed') {
|
||||||
dialog.set_values({
|
dialog.set_values({
|
||||||
'number': number,
|
'number': number,
|
||||||
'message': emailed
|
'message': emailed
|
||||||
|
@ -84,7 +84,7 @@
|
|||||||
"fieldname": "naming_series",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Series",
|
"label": "Series",
|
||||||
"options": "LP-",
|
"options": "HLC-LAB-.YYYY.-",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"report_hide": 1,
|
"report_hide": 1,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
@ -197,11 +197,10 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"options": "Draft\nCompleted\nApproved\nRejected\nCancelled",
|
"options": "Draft\nCompleted\nApproved\nRejected\nCancelled",
|
||||||
"print_hide": 1,
|
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 1,
|
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -249,8 +248,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "result_date",
|
"fieldname": "result_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 1,
|
|
||||||
"label": "Result Date",
|
"label": "Result Date",
|
||||||
|
"read_only": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -354,7 +353,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "sb_normal",
|
"fieldname": "sb_normal",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Compound Test Result"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "normal_test_items",
|
"fieldname": "normal_test_items",
|
||||||
@ -369,11 +369,13 @@
|
|||||||
{
|
{
|
||||||
"depends_on": "descriptive_toggle",
|
"depends_on": "descriptive_toggle",
|
||||||
"fieldname": "organisms_section",
|
"fieldname": "organisms_section",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Organism Test Result"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "sb_sensitivity",
|
"fieldname": "sb_sensitivity",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Sensitivity Test Result"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "sensitivity_test_items",
|
"fieldname": "sensitivity_test_items",
|
||||||
@ -383,8 +385,10 @@
|
|||||||
"report_hide": 1
|
"report_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"collapsible": 1,
|
||||||
"fieldname": "sb_comments",
|
"fieldname": "sb_comments",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Comments"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "lab_test_comment",
|
"fieldname": "lab_test_comment",
|
||||||
@ -531,7 +535,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "sb_descriptive",
|
"fieldname": "sb_descriptive",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Descriptive Test Result"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -550,7 +555,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-16 13:35:24.811062",
|
"modified": "2020-07-30 18:18:38.516215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Lab Test",
|
"name": "Lab Test",
|
||||||
|
@ -6,23 +6,24 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import getdate, cstr
|
from frappe.utils import getdate, cstr, get_link_to_form
|
||||||
|
|
||||||
class LabTest(Document):
|
class LabTest(Document):
|
||||||
|
def validate(self):
|
||||||
|
if not self.is_new():
|
||||||
|
self.set_secondary_uom_result()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
|
self.validate_result_values()
|
||||||
self.db_set('submitted_date', getdate())
|
self.db_set('submitted_date', getdate())
|
||||||
self.db_set('status', 'Completed')
|
self.db_set('status', 'Completed')
|
||||||
insert_lab_test_to_medical_record(self)
|
insert_lab_test_to_medical_record(self)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
delete_lab_test_from_medical_record(self)
|
|
||||||
self.db_set('status', 'Cancelled')
|
self.db_set('status', 'Cancelled')
|
||||||
|
delete_lab_test_from_medical_record(self)
|
||||||
self.reload()
|
self.reload()
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
if not self.is_new():
|
|
||||||
self.set_secondary_uom_result()
|
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
if self.sensitivity_test_items:
|
if self.sensitivity_test_items:
|
||||||
sensitivity = sorted(self.sensitivity_test_items, key=lambda x: x.antibiotic_sensitivity)
|
sensitivity = sorted(self.sensitivity_test_items, key=lambda x: x.antibiotic_sensitivity)
|
||||||
@ -51,7 +52,20 @@ class LabTest(Document):
|
|||||||
item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor)
|
item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor)
|
||||||
except:
|
except:
|
||||||
item.secondary_uom_result = ''
|
item.secondary_uom_result = ''
|
||||||
frappe.msgprint(_('Result for Secondary UOM not calculated for row #{0}'.format(item.idx)), title = _('Warning'))
|
frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated'.format(item.idx)), title = _('Warning'))
|
||||||
|
|
||||||
|
def validate_result_values(self):
|
||||||
|
if self.normal_test_items:
|
||||||
|
for item in self.normal_test_items:
|
||||||
|
if not item.result_value and not item.allow_blank and item.require_result_value:
|
||||||
|
frappe.throw(_('Row #{0}: Please enter the result value for {1}').format(
|
||||||
|
item.idx, frappe.bold(item.lab_test_name)), title=_('Mandatory Results'))
|
||||||
|
|
||||||
|
if self.descriptive_test_items:
|
||||||
|
for item in self.descriptive_test_items:
|
||||||
|
if not item.result_value and not item.allow_blank and item.require_result_value:
|
||||||
|
frappe.throw(_('Row #{0}: Please enter the result value for {1}').format(
|
||||||
|
item.idx, frappe.bold(item.lab_test_particulars)), title=_('Mandatory Results'))
|
||||||
|
|
||||||
|
|
||||||
def create_test_from_template(lab_test):
|
def create_test_from_template(lab_test):
|
||||||
@ -89,7 +103,7 @@ def create_multiple(doctype, docname):
|
|||||||
lab_test_created = create_lab_test_from_encounter(docname)
|
lab_test_created = create_lab_test_from_encounter(docname)
|
||||||
|
|
||||||
if lab_test_created:
|
if lab_test_created:
|
||||||
frappe.msgprint(_('Lab Test(s) {0} created'.format(lab_test_created)))
|
frappe.msgprint(_('Lab Test(s) {0} created successfully').format(lab_test_created), indicator='green')
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(_('No Lab Tests created'))
|
frappe.msgprint(_('No Lab Tests created'))
|
||||||
|
|
||||||
@ -211,8 +225,9 @@ def create_sample_doc(template, patient, invoice, company = None):
|
|||||||
'docstatus': 0,
|
'docstatus': 0,
|
||||||
'sample': template.sample
|
'sample': template.sample
|
||||||
})
|
})
|
||||||
|
|
||||||
if sample_exists:
|
if sample_exists:
|
||||||
# Update Sample Collection by adding quantity
|
# update sample collection by adding quantity
|
||||||
sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0])
|
sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0])
|
||||||
quantity = int(sample_collection.sample_qty) + int(template.sample_qty)
|
quantity = int(sample_collection.sample_qty) + int(template.sample_qty)
|
||||||
if template.sample_details:
|
if template.sample_details:
|
||||||
@ -238,7 +253,7 @@ def create_sample_doc(template, patient, invoice, company = None):
|
|||||||
sample_collection.company = company
|
sample_collection.company = company
|
||||||
|
|
||||||
if template.sample_details:
|
if template.sample_details:
|
||||||
sample_collection.sample_details = 'Test :' + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details
|
sample_collection.sample_details = _('Test :') + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details
|
||||||
sample_collection.save(ignore_permissions=True)
|
sample_collection.save(ignore_permissions=True)
|
||||||
|
|
||||||
return sample_collection
|
return sample_collection
|
||||||
@ -248,26 +263,31 @@ def create_sample_collection(lab_test, template, patient, invoice):
|
|||||||
sample_collection = create_sample_doc(template, patient, invoice, lab_test.company)
|
sample_collection = create_sample_doc(template, patient, invoice, lab_test.company)
|
||||||
if sample_collection:
|
if sample_collection:
|
||||||
lab_test.sample = sample_collection.name
|
lab_test.sample = sample_collection.name
|
||||||
|
sample_collection_doc = get_link_to_form('Sample Collection', sample_collection.name)
|
||||||
|
frappe.msgprint(_('Sample Collection {0} has been created').format(sample_collection_doc),
|
||||||
|
title=_('Sample Collection'), indicator='green')
|
||||||
return lab_test
|
return lab_test
|
||||||
|
|
||||||
def load_result_format(lab_test, template, prescription, invoice):
|
def load_result_format(lab_test, template, prescription, invoice):
|
||||||
if template.lab_test_template_type == 'Single':
|
if template.lab_test_template_type == 'Single':
|
||||||
create_normals(template, lab_test)
|
create_normals(template, lab_test)
|
||||||
|
|
||||||
elif template.lab_test_template_type == 'Compound':
|
elif template.lab_test_template_type == 'Compound':
|
||||||
create_compounds(template, lab_test, False)
|
create_compounds(template, lab_test, False)
|
||||||
|
|
||||||
elif template.lab_test_template_type == 'Descriptive':
|
elif template.lab_test_template_type == 'Descriptive':
|
||||||
create_descriptives(template, lab_test)
|
create_descriptives(template, lab_test)
|
||||||
|
|
||||||
elif template.lab_test_template_type == 'Grouped':
|
elif template.lab_test_template_type == 'Grouped':
|
||||||
# Iterate for each template in the group and create one result for all.
|
# Iterate for each template in the group and create one result for all.
|
||||||
for lab_test_group in template.lab_test_groups:
|
for lab_test_group in template.lab_test_groups:
|
||||||
# Template_in_group = None
|
# Template_in_group = None
|
||||||
if lab_test_group.lab_test_template:
|
if lab_test_group.lab_test_template:
|
||||||
template_in_group = frappe.get_doc('Lab Test Template',
|
template_in_group = frappe.get_doc('Lab Test Template', lab_test_group.lab_test_template)
|
||||||
lab_test_group.lab_test_template)
|
|
||||||
if template_in_group:
|
if template_in_group:
|
||||||
if template_in_group.lab_test_template_type == 'Single':
|
if template_in_group.lab_test_template_type == 'Single':
|
||||||
create_normals(template_in_group, lab_test)
|
create_normals(template_in_group, lab_test)
|
||||||
|
|
||||||
elif template_in_group.lab_test_template_type == 'Compound':
|
elif template_in_group.lab_test_template_type == 'Compound':
|
||||||
normal_heading = lab_test.append('normal_test_items')
|
normal_heading = lab_test.append('normal_test_items')
|
||||||
normal_heading.lab_test_name = template_in_group.lab_test_name
|
normal_heading.lab_test_name = template_in_group.lab_test_name
|
||||||
@ -275,6 +295,7 @@ def load_result_format(lab_test, template, prescription, invoice):
|
|||||||
normal_heading.allow_blank = 1
|
normal_heading.allow_blank = 1
|
||||||
normal_heading.template = template_in_group.name
|
normal_heading.template = template_in_group.name
|
||||||
create_compounds(template_in_group, lab_test, True)
|
create_compounds(template_in_group, lab_test, True)
|
||||||
|
|
||||||
elif template_in_group.lab_test_template_type == 'Descriptive':
|
elif template_in_group.lab_test_template_type == 'Descriptive':
|
||||||
descriptive_heading = lab_test.append('descriptive_test_items')
|
descriptive_heading = lab_test.append('descriptive_test_items')
|
||||||
descriptive_heading.lab_test_name = template_in_group.lab_test_name
|
descriptive_heading.lab_test_name = template_in_group.lab_test_name
|
||||||
@ -282,6 +303,7 @@ def load_result_format(lab_test, template, prescription, invoice):
|
|||||||
descriptive_heading.allow_blank = 1
|
descriptive_heading.allow_blank = 1
|
||||||
descriptive_heading.template = template_in_group.name
|
descriptive_heading.template = template_in_group.name
|
||||||
create_descriptives(template_in_group, lab_test)
|
create_descriptives(template_in_group, lab_test)
|
||||||
|
|
||||||
else: # Lab Test Group - Add New Line
|
else: # Lab Test Group - Add New Line
|
||||||
normal = lab_test.append('normal_test_items')
|
normal = lab_test.append('normal_test_items')
|
||||||
normal.lab_test_name = lab_test_group.group_event
|
normal.lab_test_name = lab_test_group.group_event
|
||||||
@ -292,6 +314,7 @@ def load_result_format(lab_test, template, prescription, invoice):
|
|||||||
normal.allow_blank = lab_test_group.allow_blank
|
normal.allow_blank = lab_test_group.allow_blank
|
||||||
normal.require_result_value = 1
|
normal.require_result_value = 1
|
||||||
normal.template = template.name
|
normal.template = template.name
|
||||||
|
|
||||||
if template.lab_test_template_type != 'No Result':
|
if template.lab_test_template_type != 'No Result':
|
||||||
if prescription:
|
if prescription:
|
||||||
lab_test.prescription = prescription
|
lab_test.prescription = prescription
|
||||||
@ -302,9 +325,10 @@ def load_result_format(lab_test, template, prescription, invoice):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_employee_by_user_id(user_id):
|
def get_employee_by_user_id(user_id):
|
||||||
emp_id = frappe.db.get_value('Employee', { 'user_id': user_id })
|
emp_id = frappe.db.exists('Employee', { 'user_id': user_id })
|
||||||
employee = frappe.get_doc('Employee', emp_id)
|
if emp_id:
|
||||||
return employee
|
return frappe.get_doc('Employee', emp_id)
|
||||||
|
return None
|
||||||
|
|
||||||
def insert_lab_test_to_medical_record(doc):
|
def insert_lab_test_to_medical_record(doc):
|
||||||
table_row = False
|
table_row = False
|
||||||
@ -325,7 +349,7 @@ def insert_lab_test_to_medical_record(doc):
|
|||||||
table_row += ' ' + frappe.bold(_('Lab Test Result: ')) + item.result_value
|
table_row += ' ' + frappe.bold(_('Lab Test Result: ')) + item.result_value
|
||||||
|
|
||||||
if item.normal_range:
|
if item.normal_range:
|
||||||
table_row += ' ' + _('Normal Range:') + item.normal_range
|
table_row += ' ' + _('Normal Range: ') + item.normal_range
|
||||||
table_row += ' ' + comment
|
table_row += ' ' + comment
|
||||||
|
|
||||||
elif doc.descriptive_test_items:
|
elif doc.descriptive_test_items:
|
||||||
@ -356,7 +380,7 @@ def insert_lab_test_to_medical_record(doc):
|
|||||||
medical_record.save(ignore_permissions = True)
|
medical_record.save(ignore_permissions = True)
|
||||||
|
|
||||||
def delete_lab_test_from_medical_record(self):
|
def delete_lab_test_from_medical_record(self):
|
||||||
medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name= %s', (self.name))
|
medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name=%s', (self.name))
|
||||||
|
|
||||||
if medical_record_id and medical_record_id[0][0]:
|
if medical_record_id and medical_record_id[0][0]:
|
||||||
frappe.delete_doc('Patient Medical Record', medical_record_id[0][0])
|
frappe.delete_doc('Patient Medical Record', medical_record_id[0][0])
|
||||||
|
@ -3,13 +3,16 @@
|
|||||||
*/
|
*/
|
||||||
frappe.listview_settings['Lab Test'] = {
|
frappe.listview_settings['Lab Test'] = {
|
||||||
add_fields: ['name', 'status', 'invoiced'],
|
add_fields: ['name', 'status', 'invoiced'],
|
||||||
filters: [['docstatus', '=', '0']],
|
filters: [['docstatus', '=', '1']],
|
||||||
get_indicator: function (doc) {
|
get_indicator: function (doc) {
|
||||||
if (doc.status == 'Approved') {
|
if (doc.status === 'Approved') {
|
||||||
return [__('Approved'), 'green', 'status, = ,Approved'];
|
return [__('Approved'), 'green', 'status, =, Approved'];
|
||||||
}
|
} else if (doc.status === 'Rejected') {
|
||||||
if (doc.status == 'Rejected') {
|
|
||||||
return [__('Rejected'), 'orange', 'status, =, Rejected'];
|
return [__('Rejected'), 'orange', 'status, =, Rejected'];
|
||||||
|
} else if (doc.status === 'Completed') {
|
||||||
|
return [__('Completed'), 'green', 'status, =, Completed'];
|
||||||
|
} else if (doc.status === 'Cancelled') {
|
||||||
|
return [__('Cancelled'), 'red', 'status, =, Cancelled'];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onload: function (listview) {
|
onload: function (listview) {
|
||||||
@ -21,7 +24,7 @@ frappe.listview_settings['Lab Test'] = {
|
|||||||
|
|
||||||
var create_multiple_dialog = function (listview) {
|
var create_multiple_dialog = function (listview) {
|
||||||
var dialog = new frappe.ui.Dialog({
|
var dialog = new frappe.ui.Dialog({
|
||||||
title: 'Create Multiple Lab Test',
|
title: 'Create Multiple Lab Tests',
|
||||||
width: 100,
|
width: 100,
|
||||||
fields: [
|
fields: [
|
||||||
{ fieldtype: 'Link', label: 'Patient', fieldname: 'patient', options: 'Patient', reqd: 1 },
|
{ fieldtype: 'Link', label: 'Patient', fieldname: 'patient', options: 'Patient', reqd: 1 },
|
||||||
@ -41,7 +44,7 @@ var create_multiple_dialog = function (listview) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
primary_action_label: __('Create Lab Test'),
|
primary_action_label: __('Create'),
|
||||||
primary_action: function () {
|
primary_action: function () {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.healthcare.doctype.lab_test.lab_test.create_multiple',
|
method: 'erpnext.healthcare.doctype.lab_test.lab_test.create_multiple',
|
||||||
|
@ -3,8 +3,204 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import unittest
|
import unittest
|
||||||
|
import frappe
|
||||||
# test_records = frappe.get_test_records('Lab Test')
|
from frappe.utils import getdate, nowtime
|
||||||
|
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
|
||||||
|
from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
|
||||||
|
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
|
||||||
|
from erpnext.healthcare.doctype.patient_medical_record.test_patient_medical_record import create_lab_test_template as create_blood_test_template
|
||||||
|
|
||||||
class TestLabTest(unittest.TestCase):
|
class TestLabTest(unittest.TestCase):
|
||||||
pass
|
def test_lab_test_item(self):
|
||||||
|
lab_template = create_lab_test_template()
|
||||||
|
self.assertTrue(frappe.db.exists('Item', lab_template.item))
|
||||||
|
self.assertEqual(frappe.db.get_value('Item Price', {'item_code':lab_template.item}, 'price_list_rate'), lab_template.lab_test_rate)
|
||||||
|
|
||||||
|
lab_template.disabled = 1
|
||||||
|
lab_template.save()
|
||||||
|
self.assertEquals(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1)
|
||||||
|
|
||||||
|
lab_template.reload()
|
||||||
|
|
||||||
|
lab_template.disabled = 0
|
||||||
|
lab_template.save()
|
||||||
|
|
||||||
|
def test_descriptive_lab_test(self):
|
||||||
|
lab_template = create_lab_test_template()
|
||||||
|
|
||||||
|
# blank result value not allowed as per template
|
||||||
|
lab_test = create_lab_test(lab_template)
|
||||||
|
lab_test.descriptive_test_items[0].result_value = 12
|
||||||
|
lab_test.descriptive_test_items[2].result_value = 1
|
||||||
|
lab_test.save()
|
||||||
|
self.assertRaises(frappe.ValidationError, lab_test.submit)
|
||||||
|
|
||||||
|
def test_sample_collection(self):
|
||||||
|
frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 1)
|
||||||
|
lab_template = create_lab_test_template()
|
||||||
|
|
||||||
|
lab_test = create_lab_test(lab_template)
|
||||||
|
lab_test.descriptive_test_items[0].result_value = 12
|
||||||
|
lab_test.descriptive_test_items[1].result_value = 1
|
||||||
|
lab_test.descriptive_test_items[2].result_value = 2.3
|
||||||
|
lab_test.save()
|
||||||
|
|
||||||
|
# check sample collection created
|
||||||
|
self.assertTrue(frappe.db.exists('Sample Collection', {'sample': lab_template.sample}))
|
||||||
|
|
||||||
|
frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 0)
|
||||||
|
lab_test = create_lab_test(lab_template)
|
||||||
|
lab_test.descriptive_test_items[0].result_value = 12
|
||||||
|
lab_test.descriptive_test_items[1].result_value = 1
|
||||||
|
lab_test.descriptive_test_items[2].result_value = 2.3
|
||||||
|
lab_test.save()
|
||||||
|
|
||||||
|
# sample collection should not be created
|
||||||
|
lab_test.reload()
|
||||||
|
self.assertEquals(lab_test.sample, None)
|
||||||
|
|
||||||
|
def test_create_lab_tests_from_sales_invoice(self):
|
||||||
|
sales_invoice = create_sales_invoice()
|
||||||
|
create_multiple('Sales Invoice', sales_invoice.name)
|
||||||
|
sales_invoice.reload()
|
||||||
|
self.assertIsNotNone(sales_invoice.items[0].reference_dn)
|
||||||
|
self.assertIsNotNone(sales_invoice.items[1].reference_dn)
|
||||||
|
|
||||||
|
def test_create_lab_tests_from_patient_encounter(self):
|
||||||
|
patient_encounter = create_patient_encounter()
|
||||||
|
create_multiple('Patient Encounter', patient_encounter.name)
|
||||||
|
patient_encounter.reload()
|
||||||
|
self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created)
|
||||||
|
self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created)
|
||||||
|
|
||||||
|
|
||||||
|
def create_lab_test_template(test_sensitivity=0, sample_collection=1):
|
||||||
|
medical_department = create_medical_department()
|
||||||
|
if frappe.db.exists('Lab Test Template', 'Insulin Resistance'):
|
||||||
|
return frappe.get_doc('Lab Test Template', 'Insulin Resistance')
|
||||||
|
template = frappe.new_doc('Lab Test Template')
|
||||||
|
template.lab_test_name = 'Insulin Resistance'
|
||||||
|
template.lab_test_template_type = 'Descriptive'
|
||||||
|
template.lab_test_code = 'Insulin Resistance'
|
||||||
|
template.lab_test_group = 'Services'
|
||||||
|
template.department = medical_department
|
||||||
|
template.is_billable = 1
|
||||||
|
template.lab_test_description = 'Insulin Resistance'
|
||||||
|
template.lab_test_rate = 2000
|
||||||
|
|
||||||
|
for entry in ['FBS', 'Insulin', 'IR']:
|
||||||
|
template.append('descriptive_test_templates', {
|
||||||
|
'particulars': entry,
|
||||||
|
'allow_blank': 1 if entry=='IR' else 0
|
||||||
|
})
|
||||||
|
|
||||||
|
if test_sensitivity:
|
||||||
|
template.sensitivity = 1
|
||||||
|
|
||||||
|
if sample_collection:
|
||||||
|
template.sample = create_lab_test_sample()
|
||||||
|
template.sample_qty = 5.0
|
||||||
|
|
||||||
|
template.save()
|
||||||
|
return template
|
||||||
|
|
||||||
|
def create_medical_department():
|
||||||
|
medical_department = frappe.db.exists('Medical Department', '_Test Medical Department')
|
||||||
|
if not medical_department:
|
||||||
|
medical_department = frappe.new_doc('Medical Department')
|
||||||
|
medical_department.department = '_Test Medical Department'
|
||||||
|
medical_department.save()
|
||||||
|
medical_department = medical_department.name
|
||||||
|
|
||||||
|
return medical_department
|
||||||
|
|
||||||
|
def create_lab_test(lab_template):
|
||||||
|
patient = create_patient()
|
||||||
|
lab_test = frappe.new_doc('Lab Test')
|
||||||
|
lab_test.template = lab_template.name
|
||||||
|
lab_test.patient = patient
|
||||||
|
lab_test.patient_sex = 'Female'
|
||||||
|
lab_test.save()
|
||||||
|
|
||||||
|
return lab_test
|
||||||
|
|
||||||
|
def create_lab_test_sample():
|
||||||
|
blood_sample = frappe.db.exists('Lab Test Sample', 'Blood Sample')
|
||||||
|
if blood_sample:
|
||||||
|
return blood_sample
|
||||||
|
|
||||||
|
sample = frappe.new_doc('Lab Test Sample')
|
||||||
|
sample.sample = 'Blood Sample'
|
||||||
|
sample.sample_uom = 'U/ml'
|
||||||
|
sample.save()
|
||||||
|
|
||||||
|
return sample.name
|
||||||
|
|
||||||
|
def create_sales_invoice():
|
||||||
|
patient = create_patient()
|
||||||
|
medical_department = create_medical_department()
|
||||||
|
insulin_resistance_template = create_lab_test_template()
|
||||||
|
blood_test_template = create_blood_test_template(medical_department)
|
||||||
|
|
||||||
|
sales_invoice = frappe.new_doc('Sales Invoice')
|
||||||
|
sales_invoice.patient = patient
|
||||||
|
sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer')
|
||||||
|
sales_invoice.due_date = getdate()
|
||||||
|
sales_invoice.company = '_Test Company'
|
||||||
|
sales_invoice.debit_to = get_receivable_account('_Test Company')
|
||||||
|
|
||||||
|
tests = [insulin_resistance_template, blood_test_template]
|
||||||
|
for entry in tests:
|
||||||
|
sales_invoice.append('items', {
|
||||||
|
'item_code': entry.item,
|
||||||
|
'item_name': entry.lab_test_name,
|
||||||
|
'description': entry.lab_test_description,
|
||||||
|
'qty': 1,
|
||||||
|
'uom': 'Nos',
|
||||||
|
'conversion_factor': 1,
|
||||||
|
'income_account': get_income_account(None, '_Test Company'),
|
||||||
|
'rate': entry.lab_test_rate,
|
||||||
|
'amount': entry.lab_test_rate
|
||||||
|
})
|
||||||
|
|
||||||
|
sales_invoice.set_missing_values()
|
||||||
|
|
||||||
|
sales_invoice.submit()
|
||||||
|
return sales_invoice
|
||||||
|
|
||||||
|
def create_patient_encounter():
|
||||||
|
patient = create_patient()
|
||||||
|
medical_department = create_medical_department()
|
||||||
|
insulin_resistance_template = create_lab_test_template()
|
||||||
|
blood_test_template = create_blood_test_template(medical_department)
|
||||||
|
|
||||||
|
patient_encounter = frappe.new_doc('Patient Encounter')
|
||||||
|
patient_encounter.patient = patient
|
||||||
|
patient_encounter.practitioner = create_practitioner()
|
||||||
|
patient_encounter.encounter_date = getdate()
|
||||||
|
patient_encounter.encounter_time = nowtime()
|
||||||
|
|
||||||
|
tests = [insulin_resistance_template, blood_test_template]
|
||||||
|
for entry in tests:
|
||||||
|
patient_encounter.append('lab_test_prescription', {
|
||||||
|
'lab_test_code': entry.item,
|
||||||
|
'lab_test_name': entry.lab_test_name
|
||||||
|
})
|
||||||
|
|
||||||
|
patient_encounter.submit()
|
||||||
|
return patient_encounter
|
||||||
|
|
||||||
|
|
||||||
|
def create_practitioner():
|
||||||
|
practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner')
|
||||||
|
|
||||||
|
if not practitioner:
|
||||||
|
practitioner = frappe.new_doc('Healthcare Practitioner')
|
||||||
|
practitioner.first_name = '_Test Healthcare Practitioner'
|
||||||
|
practitioner.gender = 'Female'
|
||||||
|
practitioner.op_consulting_charge = 500
|
||||||
|
practitioner.inpatient_visit_charge = 500
|
||||||
|
practitioner.save(ignore_permissions=True)
|
||||||
|
practitioner = practitioner.name
|
||||||
|
|
||||||
|
return practitioner
|
||||||
|
@ -93,7 +93,8 @@
|
|||||||
"depends_on": "secondary_uom",
|
"depends_on": "secondary_uom",
|
||||||
"fieldname": "conversion_factor",
|
"fieldname": "conversion_factor",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Conversion Factor"
|
"label": "Conversion Factor",
|
||||||
|
"mandatory_depends_on": "secondary_uom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -106,7 +107,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-24 10:59:01.921924",
|
"modified": "2020-07-30 12:36:03.082391",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Lab Test Group Template",
|
"name": "Lab Test Group Template",
|
||||||
|
@ -34,14 +34,15 @@
|
|||||||
"descriptive_test_templates",
|
"descriptive_test_templates",
|
||||||
"section_break_group",
|
"section_break_group",
|
||||||
"lab_test_groups",
|
"lab_test_groups",
|
||||||
"medical_coding_section",
|
|
||||||
"medical_code_standard",
|
|
||||||
"medical_code",
|
|
||||||
"sb_sample_collection",
|
"sb_sample_collection",
|
||||||
"sample",
|
"sample",
|
||||||
"sample_uom",
|
"sample_uom",
|
||||||
"sample_qty",
|
"sample_qty",
|
||||||
|
"column_break_33",
|
||||||
"sample_details",
|
"sample_details",
|
||||||
|
"medical_coding_section",
|
||||||
|
"medical_code",
|
||||||
|
"medical_code_standard",
|
||||||
"worksheet_section",
|
"worksheet_section",
|
||||||
"worksheet_instructions",
|
"worksheet_instructions",
|
||||||
"result_legend_section",
|
"result_legend_section",
|
||||||
@ -112,7 +113,7 @@
|
|||||||
{
|
{
|
||||||
"default": "1",
|
"default": "1",
|
||||||
"depends_on": "eval:doc.lab_test_template_type != 'Grouped'",
|
"depends_on": "eval:doc.lab_test_template_type != 'Grouped'",
|
||||||
"description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ",
|
"description": "If unchecked, the item will not be available in Sales Invoices for billing but can be used in group test creation. ",
|
||||||
"fieldname": "is_billable",
|
"fieldname": "is_billable",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Billable",
|
"label": "Is Billable",
|
||||||
@ -128,6 +129,7 @@
|
|||||||
"mandatory_depends_on": "eval:doc.is_billable == 1"
|
"mandatory_depends_on": "eval:doc.is_billable == 1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"collapsible": 1,
|
||||||
"fieldname": "medical_coding_section",
|
"fieldname": "medical_coding_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Medical Coding"
|
"label": "Medical Coding"
|
||||||
@ -184,7 +186,7 @@
|
|||||||
"depends_on": "eval:doc.lab_test_template_type == 'Descriptive'",
|
"depends_on": "eval:doc.lab_test_template_type == 'Descriptive'",
|
||||||
"fieldname": "section_break_special",
|
"fieldname": "section_break_special",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Descriptive"
|
"label": "Descriptive Test"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -196,7 +198,7 @@
|
|||||||
"depends_on": "eval:doc.lab_test_template_type == 'Grouped'",
|
"depends_on": "eval:doc.lab_test_template_type == 'Grouped'",
|
||||||
"fieldname": "section_break_group",
|
"fieldname": "section_break_group",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Group"
|
"label": "Group Tests"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "lab_test_groups",
|
"fieldname": "lab_test_groups",
|
||||||
@ -217,7 +219,6 @@
|
|||||||
"no_copy": 1
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
|
||||||
"fieldname": "sb_sample_collection",
|
"fieldname": "sb_sample_collection",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Sample Collection"
|
"label": "Sample Collection"
|
||||||
@ -311,10 +312,14 @@
|
|||||||
"fieldname": "descriptive_test_templates",
|
"fieldname": "descriptive_test_templates",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"options": "Descriptive Test Template"
|
"options": "Descriptive Test Template"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_33",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-13 12:57:09.925436",
|
"modified": "2020-07-30 14:32:40.449818",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Lab Test Template",
|
"name": "Lab Test Template",
|
||||||
|
@ -15,7 +15,8 @@ class LabTestTemplate(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.is_billable and (not self.lab_test_rate or self.lab_test_rate <= 0.0):
|
if self.is_billable and (not self.lab_test_rate or self.lab_test_rate <= 0.0):
|
||||||
frappe.throw(_("Standard Selling Rate should be greater than zero."))
|
frappe.throw(_('Standard Selling Rate should be greater than zero.'))
|
||||||
|
|
||||||
self.validate_conversion_factor()
|
self.validate_conversion_factor()
|
||||||
self.enable_disable_item()
|
self.enable_disable_item()
|
||||||
|
|
||||||
@ -42,7 +43,9 @@ class LabTestTemplate(Document):
|
|||||||
# Remove template reference from item and disable item
|
# Remove template reference from item and disable item
|
||||||
if self.item:
|
if self.item:
|
||||||
try:
|
try:
|
||||||
frappe.delete_doc('Item', self.item)
|
item = self.item
|
||||||
|
self.db_set('item', '')
|
||||||
|
frappe.delete_doc('Item', item)
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(_('Not permitted. Please disable the Lab Test Template'))
|
frappe.throw(_('Not permitted. Please disable the Lab Test Template'))
|
||||||
|
|
||||||
@ -63,26 +66,26 @@ class LabTestTemplate(Document):
|
|||||||
'standard_rate': self.lab_test_rate,
|
'standard_rate': self.lab_test_rate,
|
||||||
'description': self.lab_test_description
|
'description': self.lab_test_description
|
||||||
})
|
})
|
||||||
item.save()
|
item.flags.ignore_mandatory = True
|
||||||
|
item.save(ignore_permissions=True)
|
||||||
|
|
||||||
def item_price_exists(self):
|
def item_price_exists(self):
|
||||||
item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': self.lab_test_code})
|
item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': self.lab_test_code})
|
||||||
if item_price:
|
if item_price:
|
||||||
return item_price[0][0]
|
return item_price[0][0]
|
||||||
else:
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
def validate_conversion_factor(self):
|
def validate_conversion_factor(self):
|
||||||
if self.lab_test_template_type == "Single" and self.secondary_uom and not self.conversion_factor:
|
if self.lab_test_template_type == 'Single' and self.secondary_uom and not self.conversion_factor:
|
||||||
frappe.throw(_("Conversion Factor is mandatory"))
|
frappe.throw(_('Conversion Factor is mandatory'))
|
||||||
if self.lab_test_template_type == "Compound":
|
if self.lab_test_template_type == 'Compound':
|
||||||
for item in self.normal_test_templates:
|
for item in self.normal_test_templates:
|
||||||
if item.secondary_uom and not item.conversion_factor:
|
if item.secondary_uom and not item.conversion_factor:
|
||||||
frappe.throw(_("Conversion Factor is mandatory"))
|
frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(item.idx))
|
||||||
if self.lab_test_template_type == "Grouped":
|
if self.lab_test_template_type == 'Grouped':
|
||||||
for group in self.lab_test_groups:
|
for group in self.lab_test_groups:
|
||||||
if group.template_or_new_line == "Add New Line" and group.secondary_uom and not group.conversion_factor:
|
if group.template_or_new_line == 'Add New Line' and group.secondary_uom and not group.conversion_factor:
|
||||||
frappe.throw(_("Conversion Factor is mandatory"))
|
frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(group.idx))
|
||||||
|
|
||||||
|
|
||||||
def create_item_from_template(doc):
|
def create_item_from_template(doc):
|
||||||
@ -101,9 +104,9 @@ def create_item_from_template(doc):
|
|||||||
'include_item_in_manufacturing': 0,
|
'include_item_in_manufacturing': 0,
|
||||||
'show_in_website': 0,
|
'show_in_website': 0,
|
||||||
'is_pro_applicable': 0,
|
'is_pro_applicable': 0,
|
||||||
'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled,
|
'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled,
|
||||||
'stock_uom': uom
|
'stock_uom': uom
|
||||||
}).insert(ignore_permissions = True, ignore_mandatory = True)
|
}).insert(ignore_permissions=True, ignore_mandatory=True)
|
||||||
|
|
||||||
# Insert item price
|
# Insert item price
|
||||||
if doc.is_billable and doc.lab_test_rate != 0.0:
|
if doc.is_billable and doc.lab_test_rate != 0.0:
|
||||||
@ -123,7 +126,7 @@ def make_item_price(item, price_list_name, item_price):
|
|||||||
'price_list': price_list_name,
|
'price_list': price_list_name,
|
||||||
'item_code': item,
|
'item_code': item,
|
||||||
'price_list_rate': item_price
|
'price_list_rate': item_price
|
||||||
}).insert(ignore_permissions = True, ignore_mandatory = True)
|
}).insert(ignore_permissions=True, ignore_mandatory=True)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def change_test_code_from_template(lab_test_code, doc):
|
def change_test_code_from_template(lab_test_code, doc):
|
||||||
@ -132,8 +135,8 @@ def change_test_code_from_template(lab_test_code, doc):
|
|||||||
if frappe.db.exists({'doctype': 'Item', 'item_code': lab_test_code}):
|
if frappe.db.exists({'doctype': 'Item', 'item_code': lab_test_code}):
|
||||||
frappe.throw(_('Lab Test Item {0} already exist').format(lab_test_code))
|
frappe.throw(_('Lab Test Item {0} already exist').format(lab_test_code))
|
||||||
else:
|
else:
|
||||||
rename_doc('Item', doc.name, lab_test_code, ignore_permissions = True)
|
rename_doc('Item', doc.name, lab_test_code, ignore_permissions=True)
|
||||||
frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_code', lab_test_code)
|
frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_code', lab_test_code)
|
||||||
frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_name', lab_test_code)
|
frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_name', lab_test_code)
|
||||||
rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions = True)
|
rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions=True)
|
||||||
return lab_test_code
|
return lab_test_code
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'template',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'label': _('Lab Tests'),
|
||||||
|
'items': ['Lab Test']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -3,5 +3,5 @@
|
|||||||
*/
|
*/
|
||||||
frappe.listview_settings['Lab Test Template'] = {
|
frappe.listview_settings['Lab Test Template'] = {
|
||||||
add_fields: ['lab_test_name', 'lab_test_code', 'lab_test_rate'],
|
add_fields: ['lab_test_name', 'lab_test_code', 'lab_test_rate'],
|
||||||
filters: [['disabled', '=', 0]]
|
filters: [['disabled', '=', 'No']]
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
import json
|
import json
|
||||||
from frappe.utils import getdate, get_time
|
from frappe.utils import getdate, get_time, flt
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe import _
|
from frappe import _
|
||||||
import datetime
|
import datetime
|
||||||
@ -45,7 +45,7 @@ class PatientAppointment(Document):
|
|||||||
|
|
||||||
def validate_overlaps(self):
|
def validate_overlaps(self):
|
||||||
end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) \
|
end_time = datetime.datetime.combine(getdate(self.appointment_date), get_time(self.appointment_time)) \
|
||||||
+ datetime.timedelta(minutes=float(self.duration))
|
+ datetime.timedelta(minutes=flt(self.duration))
|
||||||
|
|
||||||
overlaps = frappe.db.sql("""
|
overlaps = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
|
@ -34,7 +34,7 @@ class TestPatientMedicalRecord(unittest.TestCase):
|
|||||||
self.assertTrue(medical_rec)
|
self.assertTrue(medical_rec)
|
||||||
|
|
||||||
template = create_lab_test_template(medical_department)
|
template = create_lab_test_template(medical_department)
|
||||||
lab_test = create_lab_test(template, patient)
|
lab_test = create_lab_test(template.name, patient)
|
||||||
# check for lab test
|
# check for lab test
|
||||||
medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name})
|
medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name})
|
||||||
self.assertTrue(medical_rec)
|
self.assertTrue(medical_rec)
|
||||||
@ -66,7 +66,7 @@ def create_vital_signs(appointment):
|
|||||||
|
|
||||||
def create_lab_test_template(medical_department):
|
def create_lab_test_template(medical_department):
|
||||||
if frappe.db.exists('Lab Test Template', 'Blood Test'):
|
if frappe.db.exists('Lab Test Template', 'Blood Test'):
|
||||||
return 'Blood Test'
|
return frappe.get_doc('Lab Test Template', 'Blood Test')
|
||||||
|
|
||||||
template = frappe.new_doc('Lab Test Template')
|
template = frappe.new_doc('Lab Test Template')
|
||||||
template.lab_test_name = 'Blood Test'
|
template.lab_test_name = 'Blood Test'
|
||||||
@ -76,7 +76,7 @@ def create_lab_test_template(medical_department):
|
|||||||
template.is_billable = 1
|
template.is_billable = 1
|
||||||
template.lab_test_rate = 2000
|
template.lab_test_rate = 2000
|
||||||
template.save()
|
template.save()
|
||||||
return template.name
|
return template
|
||||||
|
|
||||||
def create_lab_test(template, patient):
|
def create_lab_test(template, patient):
|
||||||
lab_test = frappe.new_doc('Lab Test')
|
lab_test = frappe.new_doc('Lab Test')
|
||||||
|
@ -44,11 +44,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-01-31 12:21:45.975488",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Practitioner Schedule",
|
"name": "Practitioner Schedule",
|
||||||
"owner": "rmehta@gmail.com",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"create": 1,
|
"create": 1,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user