Merge branch 'develop' of https://github.com/frappe/erpnext into mpesa-integration

This commit is contained in:
Mangesh-Khairnar 2020-09-28 02:10:53 +05:30
commit 98658603dd
172 changed files with 3635 additions and 3909 deletions

View File

@ -16,7 +16,7 @@
ERPNext as a monolith includes the following areas for managing businesses:
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. [Sales](https://erpnext.com/open-source-sales-purchase)
1. [Purchase](https://erpnext.com/open-source-sales-purchase)

View File

@ -43,7 +43,7 @@
{
"hidden": 0,
"label": "Bank Statement",
"links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
"links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
@ -98,7 +98,7 @@
"idx": 0,
"is_standard": 1,
"label": "Accounting",
"modified": "2020-09-03 10:37:07.865801",
"modified": "2020-09-09 11:45:33.766400",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
@ -147,11 +147,6 @@
"link_to": "Trial Balance",
"type": "Report"
},
{
"label": "Point of Sale",
"link_to": "point-of-sale",
"type": "Page"
},
{
"label": "Dashboard",
"link_to": "Accounts",

View File

@ -55,7 +55,7 @@ class BankStatementTransactionEntry(Document):
def populate_payment_entries(self):
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):
frappe.throw(_("Transactions already retreived from the statement"))
@ -65,7 +65,7 @@ class BankStatementTransactionEntry(Document):
if self.bank_settings:
mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items
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:
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"]))
@ -398,20 +398,21 @@ def get_transaction_info(headers, header_index, row):
transaction[header] = ""
return transaction
def get_transaction_entries(filename, headers):
def get_transaction_entries(file_url, headers):
header_index = {}
rows, transactions = [], []
if (filename.lower().endswith("xlsx")):
if (file_url.lower().endswith("xlsx")):
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
rows = read_xlsx_file_from_attached_file(file_id=filename)
elif (filename.lower().endswith("csv")):
rows = read_xlsx_file_from_attached_file(file_url=file_url)
elif (file_url.lower().endswith("csv")):
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()
with open(filepath,'rb') as csvfile:
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)
else:
frappe.throw(_("Only .csv and .xlsx files are supported currently"))

View File

@ -38,8 +38,8 @@
"reqd": 1
}
],
"modified": "2020-06-18 20:27:42.615842",
"modified_by": "ahmad@havenir.com",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Item Tax Template",
"owner": "Administrator",

View File

@ -52,7 +52,7 @@
"modified_by": "Administrator",
"module": "Accounts",
"name": "Mode of Payment",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"create": 1,

View File

@ -12,9 +12,10 @@ frappe.ui.form.on('Payment Entry', {
setup: function(frm) {
frm.set_query("paid_from", function() {
frm.events.validate_company(frm);
var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
return {
filters: {
"account_type": ["in", account_types],
@ -23,13 +24,16 @@ frappe.ui.form.on('Payment Entry', {
}
}
});
frm.set_query("party_type", function() {
frm.events.validate_company(frm);
return{
filters: {
"name": ["in", Object.keys(frappe.boot.party_account_types)],
}
}
});
frm.set_query("party_bank_account", function() {
return {
filters: {
@ -39,6 +43,7 @@ frappe.ui.form.on('Payment Entry', {
}
}
});
frm.set_query("bank_account", function() {
return {
filters: {
@ -47,6 +52,7 @@ frappe.ui.form.on('Payment Entry', {
}
}
});
frm.set_query("contact_person", function() {
if (frm.doc.party) {
return {
@ -58,10 +64,12 @@ frappe.ui.form.on('Payment Entry', {
};
}
});
frm.set_query("paid_to", function() {
frm.events.validate_company(frm);
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
return {
filters: {
"account_type": ["in", account_types],
@ -150,6 +158,12 @@ frappe.ui.form.on('Payment Entry', {
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) {
frm.events.hide_unhide_fields(frm);
frm.events.set_dynamic_labels(frm);

View File

@ -1,4 +1,5 @@
{
"actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2016-06-01 14:38:51.012597",
@ -63,6 +64,7 @@
"cost_center",
"section_break_12",
"status",
"custom_remarks",
"remarks",
"column_break_16",
"letter_head",
@ -462,7 +464,8 @@
"fieldname": "remarks",
"fieldtype": "Small Text",
"label": "Remarks",
"no_copy": 1
"no_copy": 1,
"read_only_depends_on": "eval:doc.custom_remarks == 0"
},
{
"fieldname": "column_break_16",
@ -573,10 +576,18 @@
"label": "Status",
"options": "\nDraft\nSubmitted\nCancelled",
"read_only": 1
},
{
"default": "0",
"fieldname": "custom_remarks",
"fieldtype": "Check",
"label": "Custom Remarks"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"modified": "2019-12-08 13:02:30.016610",
"links": [],
"modified": "2020-09-02 13:39:43.383705",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@ -453,7 +453,7 @@ class PaymentEntry(AccountsController):
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
def set_remarks(self):
if self.remarks: return
if self.custom_remarks: return
if self.payment_type=="Internal Transfer":
remarks = [_("Amount {0} {1} transferred from {2} to {3}")

View File

@ -291,11 +291,11 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 16:15:49.089450",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Period Closing Voucher",
"owner": "jai@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"amend": 1,

View File

@ -55,14 +55,48 @@ frappe.ui.form.on('POS Closing Entry', {
},
callback: (r) => {
let pos_docs = r.message;
set_form_data(pos_docs, frm)
refresh_fields(frm)
set_html_data(frm)
set_form_data(pos_docs, frm);
refresh_fields(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', {
closing_amount: (frm, 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.net_total += flt(d.net_total);
frm.doc.total_quantity += flt(d.total_qty);
add_to_payments(d, frm);
add_to_taxes(d, frm);
refresh_payments(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 => {
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_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 {
frm.add_child("payment_reconciliation", {
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 => {
const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
if (tax) {
tax.amount += flt(t.tax_amount);
if (!remove) tax.amount += flt(t.tax_amount);
else tax.amount -= flt(t.tax_amount);
} else {
frm.add_child("taxes", {
account_head: t.account_head,

View File

@ -279,7 +279,8 @@
"fieldtype": "Check",
"label": "Is Return (Credit Note)",
"no_copy": 1,
"print_hide": 1
"print_hide": 1,
"set_only_once": 1
},
{
"fieldname": "column_break1",
@ -1578,9 +1579,10 @@
}
],
"icon": "fa fa-file-text",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-05-29 15:08:39.337385",
"modified": "2020-09-07 12:43:09.138720",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",

View File

@ -182,8 +182,9 @@ class TestPOSInvoice(unittest.TestCase):
def test_pos_returns_with_repayment(self):
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': 'Cash', 'account': 'Cash - _TC', 'amount': 500})
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500, 'default': 1})
pos.insert()
pos.submit()
@ -200,8 +201,9 @@ class TestPOSInvoice(unittest.TestCase):
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
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': 'Cash', 'account': 'Cash - _TC', 'amount': 60})
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60, 'default': 1})
pos.insert()
pos.submit()

View File

@ -24,11 +24,20 @@ class POSInvoiceMergeLog(Document):
def validate_pos_invoice_status(self):
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:
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))
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):
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]
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)
else:
credit_note = ""
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log

View File

@ -5,13 +5,14 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import cint
from frappe.utils import cint, get_link_to_form
from frappe.model.document import Document
from erpnext.controllers.status_updater import StatusUpdater
class POSOpeningEntry(StatusUpdater):
def validate(self):
self.validate_pos_profile_and_cashier()
self.validate_payment_method_account()
self.set_status()
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")):
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):
self.set_status(update=True)

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
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 frappe.model.document import Document
@ -13,7 +13,7 @@ class POSProfile(Document):
self.validate_default_profile()
self.validate_all_link_fields()
self.validate_duplicate_groups()
self.check_default_payment()
self.validate_payment_methods()
def validate_default_profile(self):
for row in self.applicable_for_users:
@ -52,14 +52,23 @@ class POSProfile(Document):
if len(customer_groups) != len(set(customer_groups)):
frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group")
def check_default_payment(self):
if self.payments:
default_mode_of_payment = [d.default for d in self.payments if d.default]
if not default_mode_of_payment:
frappe.throw(_("Set default mode of payment"))
def validate_payment_methods(self):
if not self.payments:
frappe.throw(_("Payment methods are mandatory. Please add at least one payment method."))
if len(default_mode_of_payment) > 1:
frappe.throw(_("Multiple default mode of payment is not allowed"))
default_mode_of_payment = [d.default for d in self.payments if d.default]
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):
self.set_defaults()

View File

@ -28,7 +28,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
// 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) {
if (this.frm.doc.supplier && this.frm.doc.__islocal) {
this.frm.trigger('supplier');
}
},

View File

@ -405,8 +405,6 @@ class PurchaseInvoice(BuyingController):
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
def make_gl_entries(self, gl_entries=None):
if not self.grand_total:
return
if not gl_entries:
gl_entries = self.get_gl_entries()

View File

@ -210,7 +210,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2020-03-12 14:53:47.679439",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Taxes and Charges",

View File

@ -78,7 +78,7 @@
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Taxes and Charges Template",
"owner": "wasim@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"email": 1,

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe, erpnext
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 erpnext.accounts.party import get_party_account, get_due_date
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")
if not account:
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 {
"account": account
}
@ -1612,18 +1612,16 @@ def update_multi_mode_option(doc, pos_profile):
payment.type = payment_mode.type
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'):
pos_payment_method = pos_payment_method.as_dict()
payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
if payment_mode:
payment_mode[0].default = pos_payment_method.default
append_payment(payment_mode[0])
if not payment_mode:
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {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):
return frappe.db.sql("""

View File

@ -320,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['is_cancelled'] = 1
entry['posting_date'] = today()
if entry['debit'] or entry['credit']:
make_entry(entry, adv_adj, "Yes")

View File

@ -72,7 +72,7 @@ erpnext.accounts.bankReconciliation = class BankReconciliation {
check_plaid_status() {
const me = this;
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
if (r && r.enabled == "1") {
if (r && r.enabled === "1") {
me.plaid_status = "active"
} else {
me.plaid_status = "inactive"
@ -139,7 +139,7 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
}
make() {
const me = this;
const me = this;
new frappe.ui.FileUploader({
method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
allow_multiple: 0,
@ -214,31 +214,35 @@ erpnext.accounts.bankTransactionSync = class bankTransactionSync {
init_config() {
const me = this;
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration')
.then(result => {
me.plaid_env = result.plaid_env;
me.plaid_public_key = result.plaid_public_key;
me.client_name = result.client_name;
me.sync_transactions()
})
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration')
.then(result => {
me.plaid_env = result.plaid_env;
me.client_name = result.client_name;
me.link_token = result.link_token;
me.sync_transactions();
})
}
sync_transactions() {
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', {
bank: v['bank'],
bank: r.bank,
bank_account: me.parent.bank_account,
freeze: true
})
.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 = `
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
<h5 class="text-muted">${result_title}</h5>
</div>`
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
<h5 class="text-muted">${result_title}</h5>
</div>`
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',
{bank_transaction: data, freeze:true, freeze_message:__("Finding linked payments")}
{ bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") }
).then((result) => {
me.make_dialog(result)
})

View File

@ -34,7 +34,7 @@ frappe.query_reports["Accounts Receivable"] = {
filters: {
'company': company
}
}
};
}
},
{

View File

@ -617,9 +617,19 @@ class ReceivablePayableReport(object):
elif party_type_field=="supplier":
self.add_supplier_filters(conditions, values)
if self.filters.cost_center:
self.get_cost_center_conditions(conditions)
self.add_accounting_dimensions_filters(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):
if self.filters.get('group_by_party'):
return "order by party, posting_date"

View File

@ -268,9 +268,9 @@ class GrossProfitGenerator(object):
def get_last_purchase_rate(self, item_code, row):
condition = ''
if row.project:
condition += " AND a.project='%s'" % (row.project)
condition += " AND a.project=%s" % (frappe.db.escape(row.project))
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:
condition += " AND modified='%s'" % (self.filters.to_date)

View File

@ -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) {
frm.trigger("toggle_reference_doc");
// 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;
},
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");
}
}
});

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, erpnext, math, json
from frappe import _
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 erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.assets.doctype.asset.depreciation \
@ -83,6 +83,11 @@ class Asset(AccountsController):
if not self.available_for_use_date:
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):
if not self.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):
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else '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 = [{
'asset': self.name,
'asset_name': self.asset_name,
@ -146,7 +155,7 @@ class Asset(AccountsController):
'assets': assets,
'purpose': 'Receipt',
'company': self.company,
'transaction_date': getdate(self.purchase_date),
'transaction_date': transaction_date,
'reference_doctype': reference_doctype,
'reference_name': reference_docname
}).insert()
@ -294,7 +303,7 @@ class Asset(AccountsController):
if not row.depreciation_start_date:
if not self.available_for_use_date:
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
row.depreciation_start_date = self.available_for_use_date
row.depreciation_start_date = get_last_day(self.available_for_use_date)
if not self.is_existing_asset:
self.opening_accumulated_depreciation = 0

View File

@ -371,19 +371,18 @@ class TestAsset(unittest.TestCase):
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
asset.calculate_depreciation = 1
asset.available_for_use_date = nowdate()
asset.purchase_date = nowdate()
asset.available_for_use_date = '2020-01-01'
asset.purchase_date = '2020-01-01'
asset.append("finance_books", {
"expected_value_after_useful_life": 10000,
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 10,
"depreciation_start_date": nowdate()
"total_number_of_depreciations": 10,
"frequency_of_depreciation": 1
})
asset.insert()
asset.submit()
post_depreciation_entries(date=add_months(nowdate(), 10))
post_depreciation_entries(date=add_months('2020-01-01', 4))
scrap_asset(asset.name)
@ -392,9 +391,9 @@ class TestAsset(unittest.TestCase):
self.assertTrue(asset.journal_entry_for_scrap)
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 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`
@ -466,8 +465,7 @@ class TestAsset(unittest.TestCase):
"expected_value_after_useful_life": 10000,
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 10,
"depreciation_start_date": "2020-06-06"
"frequency_of_depreciation": 10
})
asset.insert()
accumulated_depreciation_after_full_schedule = \

View File

@ -1,347 +1,99 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-05-08 14:44:37.095570",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"creation": "2018-05-08 14:44:37.095570",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"finance_book",
"depreciation_method",
"total_number_of_depreciations",
"column_break_5",
"frequency_of_depreciation",
"depreciation_start_date",
"expected_value_after_useful_life",
"value_after_depreciation",
"rate_of_depreciation"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"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
},
"fieldname": "finance_book",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Finance Book",
"options": "Finance Book"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
},
"fieldname": "depreciation_method",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Depreciation Method",
"options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"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
},
"fieldname": "total_number_of_depreciations",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Total Number of Depreciations",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"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
},
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"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
},
"fieldname": "frequency_of_depreciation",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Frequency of Depreciation (Months)",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
},
"depends_on": "eval:parent.doctype == 'Asset'",
"fieldname": "depreciation_start_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Depreciation Posting Date",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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
},
"default": "0",
"depends_on": "eval:parent.doctype == 'Asset'",
"fieldname": "expected_value_after_useful_life",
"fieldtype": "Currency",
"label": "Expected Value After Useful Life",
"options": "Company:company:default_currency"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "value_after_depreciation",
"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
},
"fieldname": "value_after_depreciation",
"fieldtype": "Currency",
"hidden": 1,
"label": "Value After Depreciation",
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"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
"depends_on": "eval:doc.depreciation_method == 'Written Down Value'",
"description": "In Percentage",
"fieldname": "rate_of_depreciation",
"fieldtype": "Percent",
"label": "Rate of Depreciation"
}
],
"has_web_view": 0,
"hide_toolbar": 0,
"idx": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-04-09 19:45:14.523488",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",
"name_case": "",
"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
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-09-16 12:11:30.631788",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -32,8 +32,7 @@ class TestAssetMovement(unittest.TestCase):
"next_depreciation_date": "2020-12-31",
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 10,
"depreciation_start_date": "2020-06-06"
"frequency_of_depreciation": 10
})
if asset.docstatus == 0:
@ -82,8 +81,7 @@ class TestAssetMovement(unittest.TestCase):
"next_depreciation_date": "2020-12-31",
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 10,
"depreciation_start_date": "2020-06-06"
"frequency_of_depreciation": 10
})
if asset.docstatus == 0:
asset.submit()

View File

@ -1084,7 +1084,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2020-07-31 14:13:44.610190",
"modified": "2020-09-14 14:36:12.418690",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
@ -1135,5 +1135,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"timeline_field": "supplier",
"title_field": "supplier"
"title_field": "supplier",
"track_changes": 1
}

View File

@ -89,7 +89,7 @@ class TestPurchaseOrder(unittest.TestCase):
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)
po = make_purchase_order(mr.name)
po.supplier = "_Test Supplier"
@ -119,7 +119,7 @@ class TestPurchaseOrder(unittest.TestCase):
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.items[0].qty = 4
po.save()
@ -145,7 +145,7 @@ class TestPurchaseOrder(unittest.TestCase):
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.items[0].qty = 4
po.save()
@ -185,7 +185,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(len(po.get('items')), 1)
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)
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)
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):
po = create_purchase_order()

View File

@ -148,11 +148,11 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2020-03-12 15:43:53.862897",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item Supplied",
"owner": "dhanalekshmi@webnotestech.com",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"

View File

@ -188,11 +188,11 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2020-04-10 18:09:33.997618",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Receipt Item Supplied",
"owner": "wasim@webnotestech.com",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",

View File

@ -178,6 +178,7 @@ def make_all_scorecards(docname):
period_card = make_supplier_scorecard(docname, None)
period_card.start_date = start_date
period_card.end_date = end_date
period_card.insert(ignore_permissions=True)
period_card.submit()
scp_count = scp_count + 1
if start_date < first_start_date:

View File

@ -106,7 +106,7 @@ def make_supplier_scorecard(source_name, target_doc=None):
"doctype": "Supplier Scorecard Scoring Criteria",
"postprocess": update_criteria_fields,
}
}, target_doc, post_process)
}, target_doc, post_process, ignore_permissions=True)
return doc

View File

@ -7,6 +7,7 @@ import json
from frappe import _, throw
from frappe.utils import (today, flt, cint, fmt_money, formatdate,
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.setup.utils import get_exchange_rate
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 six import text_type
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
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
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):
"""
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.description = item.description
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 = item.stock_uom
child_item.uom = trans_item.get("uom") or 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)
if not child_item.warehouse:
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.description = item.description
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 = item.stock_uom
child_item.uom = trans_item.get("uom") or 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_amount = 1 # Initiallize value will update in parent validation
set_child_tax_template_and_map(item, child_item, p_doc)
return child_item
def check_and_delete_children(parent, data):
def validate_and_delete_children(parent, data):
deleted_children = []
updated_item_names = [d.get("docname") for d in data]
for item in parent.items:
@ -1221,18 +1238,40 @@ def check_and_delete_children(parent, data):
@frappe.whitelist()
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:
doc.check_permission(perm_type)
except:
action = "add" if perm_type == 'create' else "update"
frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions"))
except frappe.PermissionError:
actions = { 'create': 'add', 'write': 'update', 'cancel': 'remove' }
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):
if parent_doctype == "Sales Order":
return set_sales_order_defaults(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)
new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults
return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row)
def validate_quantity(child_item, d):
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']
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:
new_child_flag = False
if not d.get("docname"):
new_child_flag = True
check_permissions(parent, 'create')
check_doc_permissions(parent, 'create')
child_item = get_new_child_item(d)
else:
check_permissions(parent, 'write')
check_doc_permissions(parent, 'write')
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_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_uom, new_uom = child_item.get("uom"), d.get("uom")
if parent_doctype == 'Sales Order':
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
qty_unchanged = prev_qty == new_qty
uom_unchanged = prev_uom == new_uom
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
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
validate_quantity(child_item, d)
@ -1291,6 +1333,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
else:
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':
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()
if parent.is_subcontracted == "Yes":
parent.update_reserved_qty_for_subcontract()
parent.create_raw_materials_supplied("supplied_items")
parent.save()
else:
parent.update_reserved_qty()
parent.update_project()
parent.update_prevdoc_status('submit')
parent.update_delivery_status()
parent.reload()
validate_workflow_conditions(parent)
parent.update_blanket_order()
parent.update_billing_percentage()
parent.set_status()

View File

@ -165,9 +165,14 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
AND company = %(company)s
AND account_currency = %(currency)s
AND `{searchfield}` LIKE %(txt)s
{mcond}
ORDER BY idx DESC, name
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(
account_types=filters.get("account_type"),
company=filters.get("company"),
@ -359,9 +364,21 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
if filters.get("is_return"):
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'):
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,
concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
{search_columns}
from `tabStock Ledger Entry` sle
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
where
@ -377,6 +394,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
group by batch_no {having_clause}
order by batch.expiry_date, sle.batch_no desc
limit %(start)s, %(page_len)s""".format(
search_columns = search_columns,
cond=cond,
match_conditions=get_match_cond(doctype),
having_clause = having_clause
@ -384,7 +402,9 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
return batch_nos
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
and item = %(item_code)s
and (name like %(txt)s
@ -394,7 +414,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
{0}
{match_conditions}
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()

View File

@ -242,7 +242,8 @@ def make_return_doc(doctype, source_name, target_doc=None):
'type': data.type,
'amount': -1 * paid_amount,
'base_amount': -1 * base_paid_amount,
'account': data.account
'account': data.account,
'default': data.default
})
if doc.is_pos:
doc.paid_amount = -1 * source.paid_amount

View File

@ -81,6 +81,7 @@ class SellingController(StockController):
party_details = _get_party_details(customer,
ignore_permissions=self.flags.ignore_permissions,
doctype=self.doctype, company=self.company,
posting_date=self.get('posting_date'),
fetch_payment_terms_template=fetch_payment_terms_template,
party_address=self.customer_address, shipping_address=self.shipping_address_name)
if not self.meta.get_field("sales_team"):

View File

@ -8,7 +8,7 @@
{
"hidden": 0,
"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,
@ -42,7 +42,7 @@
"idx": 0,
"is_standard": 1,
"label": "CRM",
"modified": "2020-05-28 13:33:52.906750",
"modified": "2020-08-11 18:55:18.238900",
"modified_by": "Administrator",
"module": "CRM",
"name": "CRM",

View File

@ -24,7 +24,7 @@
"converted_by",
"sales_stage",
"order_lost_reason",
"mins_to_first_response",
"first_response_time",
"expected_closing",
"next_contact",
"contact_by",
@ -152,13 +152,6 @@
"no_copy": 1,
"read_only": 1
},
{
"bold": 1,
"fieldname": "mins_to_first_response",
"fieldtype": "Float",
"label": "Mins to first response",
"read_only": 1
},
{
"fieldname": "expected_closing",
"fieldtype": "Date",
@ -419,12 +412,19 @@
"fieldtype": "Link",
"label": "Converted By",
"options": "User"
},
{
"bold": 1,
"fieldname": "first_response_time",
"fieldtype": "Duration",
"label": "First Response Time",
"read_only": 1
}
],
"icon": "fa fa-info-sign",
"idx": 195,
"links": [],
"modified": "2020-08-11 17:34:35.066961",
"modified": "2020-08-12 17:34:35.066961",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",

View File

@ -1,33 +1,44 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// 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": [
{
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
'reqd': 1,
"reqd": 1,
"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
'reqd': 1,
"reqd": 1,
"default": frappe.datetime.nowdate()
},
],
get_chart_data: function (columns, result) {
get_chart_data: function (_columns, result) {
return {
data: {
labels: result.map(d => d[0]),
datasets: [{
name: 'Mins to first response',
name: "First Response Time",
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;
}
}
}
}
}
};

View File

@ -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"
}
]
}

View File

@ -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

View File

@ -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"
}
]
}

View File

@ -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

View File

@ -3,15 +3,15 @@
frappe.ui.form.on('Student', {
setup: function(frm) {
frm.add_fetch("guardian", "guardian_name", "guardian_name");
frm.add_fetch("student", "title", "full_name");
frm.add_fetch("student", "gender", "gender");
frm.add_fetch("student", "date_of_birth", "date_of_birth");
frm.add_fetch('guardian', 'guardian_name', 'guardian_name');
frm.add_fetch('student', 'title', 'full_name');
frm.add_fetch('student', 'gender', 'gender');
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 {
"filters": {
"name": ["!=", doc.name]
'filters': {
'name': ['!=', doc.name]
}
};
})
@ -25,6 +25,12 @@ frappe.ui.form.on('Student', {
{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);
}
});
}
});

View File

@ -102,7 +102,6 @@
"fieldtype": "Data",
"in_global_search": 1,
"label": "Student Email Address",
"reqd": 1,
"unique": 1
},
{
@ -255,7 +254,7 @@
],
"image_field": "image",
"links": [],
"modified": "2020-07-23 18:14:06.366442",
"modified": "2020-09-07 19:28:08.914568",
"modified_by": "Administrator",
"module": "Education",
"name": "Student",

View File

@ -7,7 +7,7 @@ frappe.ui.form.on("Student Applicant", {
},
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.set_value("application_status", "Approved");
frm.save_or_update();
@ -20,10 +20,11 @@ frappe.ui.form.on("Student Applicant", {
}, '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.events.enroll(frm)
}).addClass("btn-primary");
frm.add_custom_button(__("Reject"), function() {
frm.set_value("application_status", "Rejected");
frm.save_or_update();
@ -35,7 +36,13 @@ frappe.ui.form.on("Student Applicant", {
frappe.hide_msgprint(true);
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) {

View File

@ -2,81 +2,90 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
from frappe import _
from frappe.utils.password import get_decrypted_password
from plaid import Client
from plaid.errors import APIError, ItemError
import plaid
import requests
from plaid.errors import APIError, ItemError, InvalidRequestError
import frappe
import requests
from frappe import _
class PlaidConnector():
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.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):
if public_token is None:
frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
response = self.client.Item.public_token.exchange(public_token)
access_token = response['access_token']
access_token = response["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):
try:
self.client.Auth.get(self.access_token)
print("Authentication successful.....")
except ItemError as e:
if e.code == 'ITEM_LOGIN_REQUIRED':
pass
else:
if e.code == "ITEM_LOGIN_REQUIRED":
pass
except APIError as e:
if e.code == 'PLANNED_MAINTENANCE':
pass
else:
if e.code == "PLANNED_MAINTENANCE":
pass
except requests.Timeout:
pass
except Exception as e:
print(e)
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):
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:
self.auth()
if account_id:
account_ids = [account_id]
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(**kwargs)
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))
transactions.extend(response['transactions'])
transactions.extend(response["transactions"])
return transactions
except Exception:
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))

View File

@ -4,14 +4,14 @@
frappe.provide("erpnext.integrations");
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_secret', frm.doc.enabled);
frm.toggle_reqd('plaid_public_key', 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', () => {
new erpnext.integrations.plaidLink(frm);
});
@ -22,17 +22,16 @@ frappe.ui.form.on('Plaid Settings', {
erpnext.integrations.plaidLink = class plaidLink {
constructor(parent) {
this.frm = parent;
this.product = ["transactions", "auth"];
this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js';
this.init_config();
}
init_config() {
const me = this;
me.plaid_env = me.frm.doc.plaid_env;
me.plaid_public_key = me.frm.doc.plaid_public_key;
me.client_name = frappe.boot.sitename;
me.init_plaid();
async init_config() {
this.product = ["auth", "transactions"];
this.plaid_env = this.frm.doc.plaid_env;
this.client_name = frappe.boot.sitename;
this.token = await this.frm.call("get_link_token").then(resp => resp.message);
this.init_plaid();
}
init_plaid() {
@ -69,17 +68,17 @@ erpnext.integrations.plaidLink = class plaidLink {
}
onScriptLoaded(me) {
me.linkHandler = window.Plaid.create({
me.linkHandler = Plaid.create({
clientName: me.client_name,
product: me.product,
env: me.plaid_env,
key: me.plaid_public_key,
onSuccess: me.plaid_success,
product: me.product
token: me.token,
onSuccess: me.plaid_success
});
}
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);
}
@ -87,21 +86,25 @@ erpnext.integrations.plaidLink = class plaidLink {
const me = this;
frappe.prompt({
fieldtype:"Link",
fieldtype: "Link",
options: "Company",
label:__("Company"),
fieldname:"company",
reqd:1
label: __("Company"),
fieldname: "company",
reqd: 1
}, (data) => {
me.company = data.company;
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {token: token, response: response})
.then((result) => {
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {response: response,
bank: result, company: me.company});
})
.then(() => {
frappe.show_alert({message:__("Bank accounts added"), indicator:'green'});
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {
token: token,
response: response
}).then((result) => {
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {
response: response,
bank: result,
company: me.company
});
}).then(() => {
frappe.show_alert({ message: __("Bank accounts added"), indicator: 'green' });
});
}, __("Select a company"), __("Continue"));
}
};

View File

@ -1,5 +1,4 @@
{
"actions": [],
"creation": "2018-10-25 10:02:48.656165",
"doctype": "DocType",
"editable_grid": 1,
@ -12,7 +11,6 @@
"plaid_client_id",
"plaid_secret",
"column_break_7",
"plaid_public_key",
"plaid_env"
],
"fields": [
@ -41,12 +39,6 @@
"in_list_view": 1,
"label": "Plaid Secret"
},
{
"fieldname": "plaid_public_key",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Plaid Public Key"
},
{
"fieldname": "plaid_env",
"fieldtype": "Select",
@ -69,8 +61,7 @@
}
],
"issingle": 1,
"links": [],
"modified": "2020-02-07 15:21:11.616231",
"modified": "2020-09-12 02:31:44.542385",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Plaid Settings",

View File

@ -2,30 +2,36 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
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.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.model.document import Document
from frappe.utils import add_months, formatdate, getdate, today
class PlaidSettings(Document):
pass
@staticmethod
def get_link_token():
plaid = PlaidConnector()
return plaid.get_link_token()
@frappe.whitelist()
def plaid_configuration():
def get_plaid_configuration():
if frappe.db.get_single_value("Plaid Settings", "enabled"):
plaid_settings = frappe.get_single("Plaid Settings")
return {
"plaid_public_key": plaid_settings.plaid_public_key,
"plaid_env": plaid_settings.plaid_env,
"link_token": plaid_settings.get_link_token(),
"client_name": frappe.local.site
}
else:
return "disabled"
return "disabled"
@frappe.whitelist()
def add_institution(token, response):
@ -33,6 +39,7 @@ def add_institution(token, response):
plaid = PlaidConnector()
access_token = plaid.get_access_token(token)
bank = None
if not frappe.db.exists("Bank", response["institution"]["name"]):
try:
@ -44,7 +51,6 @@ def add_institution(token, response):
bank.insert()
except Exception:
frappe.throw(frappe.get_traceback())
else:
bank = frappe.get_doc("Bank", response["institution"]["name"])
bank.plaid_access_token = access_token
@ -52,6 +58,7 @@ def add_institution(token, response):
return bank
@frappe.whitelist()
def add_bank_accounts(response, bank, company):
try:
@ -92,9 +99,8 @@ def add_bank_accounts(response, bank, company):
new_account.insert()
result.append(new_account.name)
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:
frappe.throw(frappe.get_traceback())
@ -103,6 +109,7 @@ def add_bank_accounts(response, bank, company):
return result
def add_account_type(account_type):
try:
frappe.get_doc({
@ -122,10 +129,11 @@ def add_account_subtype(account_subtype):
except Exception:
frappe.throw(frappe.get_traceback())
@frappe.whitelist()
def sync_transactions(bank, bank_account):
'''Sync transactions based on the last integration date as the start date, after the sync is completed
add the transaction date of the oldest transaction as the last integration date'''
"""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."""
last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
if last_transaction_date:
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))
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
except Exception:
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
access_token = None
@ -168,6 +176,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
return transactions
def new_bank_transaction(transaction):
result = []
@ -182,8 +191,8 @@ def new_bank_transaction(transaction):
status = "Pending" if transaction["pending"] == "True" else "Settled"
tags = []
try:
tags = []
tags += transaction["category"]
tags += ["Plaid Cat. {}".format(transaction["category_id"])]
except KeyError:
@ -216,6 +225,7 @@ def new_bank_transaction(transaction):
return result
def automatic_synchronization():
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"])
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
)

View File

@ -1,14 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# 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
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.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):
def setUp(self):
@ -31,7 +34,7 @@ class TestPlaidSettings(unittest.TestCase):
def test_plaid_disabled(self):
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):
add_account_type("brokerage")
@ -64,7 +67,7 @@ class TestPlaidSettings(unittest.TestCase):
'mask': '0000',
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
'name': 'Plaid Checking'
}],
}],
'institution': {
'institution_id': 'ins_6',
'name': 'Citi'
@ -100,7 +103,7 @@ class TestPlaidSettings(unittest.TestCase):
'mask': '0000',
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
'name': 'Plaid Checking'
}],
}],
'institution': {
'institution_id': 'ins_6',
'name': 'Citi'
@ -152,4 +155,4 @@ class TestPlaidSettings(unittest.TestCase):
new_bank_transaction(transactions)
self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)
self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1)

View File

@ -258,8 +258,8 @@
}
],
"issingle": 1,
"modified": "2020-05-28 12:32:11.384757",
"modified_by": "umair@erpnext.com",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Shopify Settings",
"owner": "Administrator",

View File

@ -117,12 +117,12 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-05-08 13:45:57.226530",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare Schedule Time Slot",
"name_case": "",
"owner": "rmehta@gmail.com",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,

View File

@ -34,10 +34,10 @@ frappe.ui.form.on('Lab Test', {
if (frm.doc.docstatus === 1 && frm.doc.status !== 'Approved' && frm.doc.status !== 'Rejected') {
frm.add_custom_button(__('Approve'), function () {
status_update(1, frm);
});
}, __('Actions'));
frm.add_custom_button(__('Reject'), function () {
status_update(0, frm);
});
}, __('Actions'));
}
}
@ -179,23 +179,6 @@ var show_lab_tests = function (frm, lab_test_list) {
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 number = frm.doc.mobile;
@ -203,7 +186,7 @@ var make_dialog = function (frm, emailed, printed) {
title: 'Send SMS',
width: 400,
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: 'message', fieldtype: 'Small Text', label: 'Message', reqd: 1 }
],
@ -217,22 +200,22 @@ var make_dialog = function (frm, emailed, printed) {
dialog.hide();
}
});
if (frm.doc.report_preference == 'Print') {
if (frm.doc.report_preference === 'Print') {
dialog.set_values({
'sms_type': 'Printed',
'result_format': 'Printed',
'number': number,
'message': printed
});
} else {
dialog.set_values({
'sms_type': 'Emailed',
'result_format': 'Emailed',
'number': number,
'message': emailed
});
}
var fd = dialog.fields_dict;
$(fd.sms_type.input).change(function () {
if (dialog.get_value('sms_type') == 'Emailed') {
$(fd.result_format.input).change(function () {
if (dialog.get_value('result_format') === 'Emailed') {
dialog.set_values({
'number': number,
'message': emailed

View File

@ -84,7 +84,7 @@
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"options": "LP-",
"options": "HLC-LAB-.YYYY.-",
"print_hide": 1,
"report_hide": 1,
"reqd": 1
@ -197,11 +197,10 @@
{
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Draft\nCompleted\nApproved\nRejected\nCancelled",
"print_hide": 1,
"read_only": 1,
"report_hide": 1,
"search_index": 1
},
{
@ -249,8 +248,8 @@
{
"fieldname": "result_date",
"fieldtype": "Date",
"hidden": 1,
"label": "Result Date",
"read_only": 1,
"search_index": 1
},
{
@ -354,7 +353,8 @@
},
{
"fieldname": "sb_normal",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Compound Test Result"
},
{
"fieldname": "normal_test_items",
@ -369,11 +369,13 @@
{
"depends_on": "descriptive_toggle",
"fieldname": "organisms_section",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Organism Test Result"
},
{
"fieldname": "sb_sensitivity",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Sensitivity Test Result"
},
{
"fieldname": "sensitivity_test_items",
@ -383,8 +385,10 @@
"report_hide": 1
},
{
"collapsible": 1,
"fieldname": "sb_comments",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Comments"
},
{
"fieldname": "lab_test_comment",
@ -531,7 +535,8 @@
},
{
"fieldname": "sb_descriptive",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Descriptive Test Result"
},
{
"default": "0",
@ -550,7 +555,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-07-16 13:35:24.811062",
"modified": "2020-07-30 18:18:38.516215",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test",

View File

@ -6,23 +6,24 @@ from __future__ import unicode_literals
import frappe
from frappe import _
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):
def validate(self):
if not self.is_new():
self.set_secondary_uom_result()
def on_submit(self):
self.validate_result_values()
self.db_set('submitted_date', getdate())
self.db_set('status', 'Completed')
insert_lab_test_to_medical_record(self)
def on_cancel(self):
delete_lab_test_from_medical_record(self)
self.db_set('status', 'Cancelled')
delete_lab_test_from_medical_record(self)
self.reload()
def validate(self):
if not self.is_new():
self.set_secondary_uom_result()
def on_update(self):
if self.sensitivity_test_items:
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)
except:
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):
@ -89,7 +103,7 @@ def create_multiple(doctype, docname):
lab_test_created = create_lab_test_from_encounter(docname)
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:
frappe.msgprint(_('No Lab Tests created'))
@ -211,8 +225,9 @@ def create_sample_doc(template, patient, invoice, company = None):
'docstatus': 0,
'sample': template.sample
})
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])
quantity = int(sample_collection.sample_qty) + int(template.sample_qty)
if template.sample_details:
@ -238,7 +253,7 @@ def create_sample_doc(template, patient, invoice, company = None):
sample_collection.company = company
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)
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)
if sample_collection:
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
def load_result_format(lab_test, template, prescription, invoice):
if template.lab_test_template_type == 'Single':
create_normals(template, lab_test)
elif template.lab_test_template_type == 'Compound':
create_compounds(template, lab_test, False)
elif template.lab_test_template_type == 'Descriptive':
create_descriptives(template, lab_test)
elif template.lab_test_template_type == 'Grouped':
# Iterate for each template in the group and create one result for all.
for lab_test_group in template.lab_test_groups:
# Template_in_group = None
if lab_test_group.lab_test_template:
template_in_group = frappe.get_doc('Lab Test Template',
lab_test_group.lab_test_template)
template_in_group = frappe.get_doc('Lab Test Template', lab_test_group.lab_test_template)
if template_in_group:
if template_in_group.lab_test_template_type == 'Single':
create_normals(template_in_group, lab_test)
elif template_in_group.lab_test_template_type == 'Compound':
normal_heading = lab_test.append('normal_test_items')
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.template = template_in_group.name
create_compounds(template_in_group, lab_test, True)
elif template_in_group.lab_test_template_type == 'Descriptive':
descriptive_heading = lab_test.append('descriptive_test_items')
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.template = template_in_group.name
create_descriptives(template_in_group, lab_test)
else: # Lab Test Group - Add New Line
normal = lab_test.append('normal_test_items')
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.require_result_value = 1
normal.template = template.name
if template.lab_test_template_type != 'No Result':
if prescription:
lab_test.prescription = prescription
@ -302,9 +325,10 @@ def load_result_format(lab_test, template, prescription, invoice):
@frappe.whitelist()
def get_employee_by_user_id(user_id):
emp_id = frappe.db.get_value('Employee', { 'user_id': user_id })
employee = frappe.get_doc('Employee', emp_id)
return employee
emp_id = frappe.db.exists('Employee', { 'user_id': user_id })
if emp_id:
return frappe.get_doc('Employee', emp_id)
return None
def insert_lab_test_to_medical_record(doc):
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
if item.normal_range:
table_row += ' ' + _('Normal Range:') + item.normal_range
table_row += ' ' + _('Normal Range: ') + item.normal_range
table_row += ' ' + comment
elif doc.descriptive_test_items:
@ -356,7 +380,7 @@ def insert_lab_test_to_medical_record(doc):
medical_record.save(ignore_permissions = True)
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]:
frappe.delete_doc('Patient Medical Record', medical_record_id[0][0])

View File

@ -3,13 +3,16 @@
*/
frappe.listview_settings['Lab Test'] = {
add_fields: ['name', 'status', 'invoiced'],
filters: [['docstatus', '=', '0']],
filters: [['docstatus', '=', '1']],
get_indicator: function (doc) {
if (doc.status == 'Approved') {
return [__('Approved'), 'green', 'status, = ,Approved'];
}
if (doc.status == 'Rejected') {
if (doc.status === 'Approved') {
return [__('Approved'), 'green', 'status, =, Approved'];
} else if (doc.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) {
@ -21,7 +24,7 @@ frappe.listview_settings['Lab Test'] = {
var create_multiple_dialog = function (listview) {
var dialog = new frappe.ui.Dialog({
title: 'Create Multiple Lab Test',
title: 'Create Multiple Lab Tests',
width: 100,
fields: [
{ 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 () {
frappe.call({
method: 'erpnext.healthcare.doctype.lab_test.lab_test.create_multiple',

View File

@ -3,8 +3,204 @@
# See license.txt
from __future__ import unicode_literals
import unittest
# test_records = frappe.get_test_records('Lab Test')
import frappe
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):
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

View File

@ -93,7 +93,8 @@
"depends_on": "secondary_uom",
"fieldname": "conversion_factor",
"fieldtype": "Float",
"label": "Conversion Factor"
"label": "Conversion Factor",
"mandatory_depends_on": "secondary_uom"
},
{
"default": "0",
@ -106,7 +107,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-06-24 10:59:01.921924",
"modified": "2020-07-30 12:36:03.082391",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Group Template",

View File

@ -34,14 +34,15 @@
"descriptive_test_templates",
"section_break_group",
"lab_test_groups",
"medical_coding_section",
"medical_code_standard",
"medical_code",
"sb_sample_collection",
"sample",
"sample_uom",
"sample_qty",
"column_break_33",
"sample_details",
"medical_coding_section",
"medical_code",
"medical_code_standard",
"worksheet_section",
"worksheet_instructions",
"result_legend_section",
@ -112,7 +113,7 @@
{
"default": "1",
"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",
"fieldtype": "Check",
"label": "Is Billable",
@ -128,6 +129,7 @@
"mandatory_depends_on": "eval:doc.is_billable == 1"
},
{
"collapsible": 1,
"fieldname": "medical_coding_section",
"fieldtype": "Section Break",
"label": "Medical Coding"
@ -184,7 +186,7 @@
"depends_on": "eval:doc.lab_test_template_type == 'Descriptive'",
"fieldname": "section_break_special",
"fieldtype": "Section Break",
"label": "Descriptive"
"label": "Descriptive Test"
},
{
"default": "0",
@ -196,7 +198,7 @@
"depends_on": "eval:doc.lab_test_template_type == 'Grouped'",
"fieldname": "section_break_group",
"fieldtype": "Section Break",
"label": "Group"
"label": "Group Tests"
},
{
"fieldname": "lab_test_groups",
@ -217,7 +219,6 @@
"no_copy": 1
},
{
"collapsible": 1,
"fieldname": "sb_sample_collection",
"fieldtype": "Section Break",
"label": "Sample Collection"
@ -311,10 +312,14 @@
"fieldname": "descriptive_test_templates",
"fieldtype": "Table",
"options": "Descriptive Test Template"
},
{
"fieldname": "column_break_33",
"fieldtype": "Column Break"
}
],
"links": [],
"modified": "2020-07-13 12:57:09.925436",
"modified": "2020-07-30 14:32:40.449818",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Template",

View File

@ -15,7 +15,8 @@ class LabTestTemplate(Document):
def validate(self):
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.enable_disable_item()
@ -42,7 +43,9 @@ class LabTestTemplate(Document):
# Remove template reference from item and disable item
if self.item:
try:
frappe.delete_doc('Item', self.item)
item = self.item
self.db_set('item', '')
frappe.delete_doc('Item', item)
except Exception:
frappe.throw(_('Not permitted. Please disable the Lab Test Template'))
@ -63,26 +66,26 @@ class LabTestTemplate(Document):
'standard_rate': self.lab_test_rate,
'description': self.lab_test_description
})
item.save()
item.flags.ignore_mandatory = True
item.save(ignore_permissions=True)
def item_price_exists(self):
item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': self.lab_test_code})
if item_price:
return item_price[0][0]
else:
return False
return False
def validate_conversion_factor(self):
if self.lab_test_template_type == "Single" and self.secondary_uom and not self.conversion_factor:
frappe.throw(_("Conversion Factor is mandatory"))
if self.lab_test_template_type == "Compound":
if self.lab_test_template_type == 'Single' and self.secondary_uom and not self.conversion_factor:
frappe.throw(_('Conversion Factor is mandatory'))
if self.lab_test_template_type == 'Compound':
for item in self.normal_test_templates:
if item.secondary_uom and not item.conversion_factor:
frappe.throw(_("Conversion Factor is mandatory"))
if self.lab_test_template_type == "Grouped":
frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(item.idx))
if self.lab_test_template_type == 'Grouped':
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:
frappe.throw(_("Conversion Factor is mandatory"))
if group.template_or_new_line == 'Add New Line' and group.secondary_uom and not group.conversion_factor:
frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(group.idx))
def create_item_from_template(doc):
@ -101,9 +104,9 @@ def create_item_from_template(doc):
'include_item_in_manufacturing': 0,
'show_in_website': 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
}).insert(ignore_permissions = True, ignore_mandatory = True)
}).insert(ignore_permissions=True, ignore_mandatory=True)
# Insert item price
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,
'item_code': item,
'price_list_rate': item_price
}).insert(ignore_permissions = True, ignore_mandatory = True)
}).insert(ignore_permissions=True, ignore_mandatory=True)
@frappe.whitelist()
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}):
frappe.throw(_('Lab Test Item {0} already exist').format(lab_test_code))
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_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

View File

@ -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']
}
]
}

View File

@ -3,5 +3,5 @@
*/
frappe.listview_settings['Lab Test Template'] = {
add_fields: ['lab_test_name', 'lab_test_code', 'lab_test_rate'],
filters: [['disabled', '=', 0]]
filters: [['disabled', '=', 'No']]
};

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
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 import _
import datetime
@ -45,7 +45,7 @@ class PatientAppointment(Document):
def validate_overlaps(self):
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("""
select

View File

@ -34,7 +34,7 @@ class TestPatientMedicalRecord(unittest.TestCase):
self.assertTrue(medical_rec)
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
medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name})
self.assertTrue(medical_rec)
@ -66,7 +66,7 @@ def create_vital_signs(appointment):
def create_lab_test_template(medical_department):
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.lab_test_name = 'Blood Test'
@ -76,7 +76,7 @@ def create_lab_test_template(medical_department):
template.is_billable = 1
template.lab_test_rate = 2000
template.save()
return template.name
return template
def create_lab_test(template, patient):
lab_test = frappe.new_doc('Lab Test')

View File

@ -44,11 +44,11 @@
}
],
"links": [],
"modified": "2020-01-31 12:21:45.975488",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Practitioner Schedule",
"owner": "rmehta@gmail.com",
"owner": "Administrator",
"permissions": [
{
"create": 1,

View File

@ -3,29 +3,29 @@
frappe.ui.form.on('Sample Collection', {
refresh: function(frm) {
if(frappe.defaults.get_default("create_sample_collection_for_lab_test")){
frm.add_custom_button(__("View Lab Tests"), function() {
frappe.route_options = {"sample": frm.doc.name};
frappe.set_route("List", "Lab Test");
if (frappe.defaults.get_default('create_sample_collection_for_lab_test')) {
frm.add_custom_button(__('View Lab Tests'), function() {
frappe.route_options = {'sample': frm.doc.name};
frappe.set_route('List', 'Lab Test');
});
}
}
});
frappe.ui.form.on("Sample Collection", "patient", function(frm) {
frappe.ui.form.on('Sample Collection', 'patient', function(frm) {
if(frm.doc.patient){
frappe.call({
"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
args: {
patient: frm.doc.patient
},
callback: function (data) {
var age = null;
if(data.message.dob){
if (data.message.dob){
age = calculate_age(data.message.dob);
}
frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age);
frappe.model.set_value(frm.doctype,frm.docname, "patient_sex", data.message.sex);
frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age);
frappe.model.set_value(frm.doctype,frm.docname, 'patient_sex', data.message.sex);
}
});
}
@ -36,5 +36,5 @@ var calculate_age = function(birth) {
var age = new Date();
age.setTime(ageMS);
var years = age.getFullYear() - 1970;
return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)';
};

View File

@ -9,8 +9,10 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"patient_details_section",
"naming_series",
"patient",
"patient_name",
"patient_age",
"patient_sex",
"column_break_4",
@ -25,15 +27,17 @@
"collected_by",
"collected_time",
"num_print",
"amended_from",
"section_break_15",
"sample_details"
"sample_details",
"amended_from"
],
"fields": [
{
"fetch_from": "patient.inpatient_record",
"fieldname": "inpatient_record",
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"label": "Inpatient Record",
"options": "Inpatient Record",
"read_only": 1
@ -42,6 +46,8 @@
"bold": 1,
"fieldname": "naming_series",
"fieldtype": "Select",
"hide_days": 1,
"hide_seconds": 1,
"label": "Series",
"no_copy": 1,
"options": "HLC-SC-.YYYY.-",
@ -52,6 +58,8 @@
"default": "0",
"fieldname": "invoiced",
"fieldtype": "Check",
"hide_days": 1,
"hide_seconds": 1,
"label": "Invoiced",
"no_copy": 1,
"read_only": 1,
@ -61,41 +69,60 @@
"fetch_from": "inpatient_record.patient",
"fieldname": "patient",
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"ignore_user_permissions": 1,
"in_standard_filter": 1,
"label": "Patient",
"options": "Patient",
"reqd": 1,
"search_index": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1
},
{
"fieldname": "patient_age",
"fieldtype": "Data",
"label": "Age"
"hide_days": 1,
"hide_seconds": 1,
"label": "Age",
"read_only": 1
},
{
"fetch_from": "patient.sex",
"fieldname": "patient_sex",
"fieldtype": "Data",
"label": "Gender"
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"label": "Gender",
"options": "Gender",
"read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"in_standard_filter": 1,
"label": "Company",
"options": "Company"
},
{
"fieldname": "section_break_6",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Sample Details"
},
{
"fieldname": "sample",
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"ignore_user_permissions": 1,
"in_list_view": 1,
"in_standard_filter": 1,
@ -108,16 +135,23 @@
"fetch_from": "sample.sample_uom",
"fieldname": "sample_uom",
"fieldtype": "Data",
"hide_days": 1,
"hide_seconds": 1,
"in_list_view": 1,
"label": "UOM"
"label": "UOM",
"read_only": 1
},
{
"fieldname": "column_break_10",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1
},
{
"fieldname": "collected_by",
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"ignore_user_permissions": 1,
"label": "Collected By",
"options": "User"
@ -125,20 +159,27 @@
{
"fieldname": "collected_time",
"fieldtype": "Datetime",
"label": "Collected Time"
"hide_days": 1,
"hide_seconds": 1,
"label": "Collected On"
},
{
"allow_on_submit": 1,
"default": "1",
"description": "Number of prints required for labelling the samples",
"fieldname": "num_print",
"fieldtype": "Int",
"label": "No. of print",
"hide_days": 1,
"hide_seconds": 1,
"label": "No. of prints",
"print_hide": 1,
"report_hide": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"label": "Amended From",
"no_copy": 1,
"options": "Sample Collection",
@ -147,25 +188,43 @@
},
{
"fieldname": "section_break_15",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1
},
{
"default": "0",
"fieldname": "sample_qty",
"fieldtype": "Float",
"hide_days": 1,
"hide_seconds": 1,
"in_list_view": 1,
"label": "Quantity"
},
{
"fieldname": "sample_details",
"fieldtype": "Long Text",
"hide_days": 1,
"hide_seconds": 1,
"ignore_xss_filter": 1,
"label": "Collection Details"
},
{
"fieldname": "patient_details_section",
"fieldtype": "Section Break",
"label": "Patient Details"
},
{
"fetch_from": "patient.patient_name",
"fieldname": "patient_name",
"fieldtype": "Data",
"label": "Patient Name",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-05-25 14:36:46.990469",
"modified": "2020-07-30 16:53:13.076104",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Sample Collection",

View File

@ -3,7 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.utils import flt
from frappe import _
class SampleCollection(Document):
pass
def validate(self):
if flt(self.sample_qty) <= 0:
frappe.throw(_('Sample Quantity cannot be negative or 0'), title=_('Invalid Quantity'))

View File

@ -4,29 +4,54 @@
frappe.query_reports["Lab Test Report"] = {
"filters": [
{
"fieldname":"from_date",
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.datetime.now_date(),
"width": "80"
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1
},
{
"fieldname":"to_date",
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.now_date()
"default": frappe.datetime.now_date(),
"reqd": 1
},
{
"fieldname":"patient",
"fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"default": frappe.defaults.get_default("Company"),
"options": "Company"
},
{
"fieldname": "template",
"label": __("Lab Test Template"),
"fieldtype": "Link",
"options": "Lab Test Template"
},
{
"fieldname": "patient",
"label": __("Patient"),
"fieldtype": "Link",
"options": "Patient"
},
{
"fieldname":"department",
"fieldname": "department",
"label": __("Medical Department"),
"fieldtype": "Link",
"options": "Medical Department"
},
{
"fieldname": "status",
"label": __("Status"),
"fieldtype": "Select",
"options": "\nCompleted\nApproved\nRejected"
},
{
"fieldname": "invoiced",
"label": __("Invoiced"),
"fieldtype": "Check"
}
]
};

View File

@ -1,30 +1,31 @@
{
"add_total_row": 1,
"creation": "2013-04-23 18:15:29",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2018-08-06 11:41:50.218737",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Report",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Lab Test",
"report_name": "Lab Test Report",
"report_type": "Script Report",
"add_total_row": 0,
"creation": "2013-04-23 18:15:29",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2020-07-30 18:53:20.102873",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Report",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Lab Test",
"report_name": "Lab Test Report",
"report_type": "Script Report",
"roles": [
{
"role": "Laboratory User"
},
},
{
"role": "Nursing User"
},
},
{
"role": "LabTest Approver"
},
},
{
"role": "Healthcare Administrator"
}

View File

@ -8,51 +8,204 @@ from frappe import msgprint, _
def execute(filters=None):
if not filters: filters = {}
lab_test_list = get_lab_test(filters)
data, columns = [], []
columns = get_columns()
lab_test_list = get_lab_tests(filters)
if not lab_test_list:
msgprint(_("No record found"))
msgprint(_('No records found'))
return columns, lab_test_list
data = []
for lab_test in lab_test_list:
row = [ lab_test.lab_test_name, lab_test.patient, lab_test.practitioner, lab_test.invoiced, lab_test.status, lab_test.result_date, lab_test.department]
row = frappe._dict({
'test': lab_test.name,
'template': lab_test.template,
'company': lab_test.company,
'patient': lab_test.patient,
'patient_name': lab_test.patient_name,
'practitioner': lab_test.practitioner,
'employee': lab_test.employee,
'status': lab_test.status,
'invoiced': lab_test.invoiced,
'result_date': lab_test.result_date,
'department': lab_test.department
})
data.append(row)
return columns, data
chart = get_chart_data(data)
report_summary = get_report_summary(data)
return columns, data, None, chart, report_summary
def get_columns():
columns = [
_("Test") + ":Data:120",
_("Patient") + ":Link/Patient:180",
_("Healthcare Practitioner") + ":Link/Healthcare Practitioner:120",
_("Invoiced") + ":Check:100",
_("Status") + ":Data:120",
_("Result Date") + ":Date:120",
_("Department") + ":Data:120",
return [
{
'fieldname': 'test',
'label': _('Lab Test'),
'fieldtype': 'Link',
'options': 'Lab Test',
'width': '120'
},
{
'fieldname': 'template',
'label': _('Lab Test Template'),
'fieldtype': 'Link',
'options': 'Lab Test Template',
'width': '120'
},
{
'fieldname': 'company',
'label': _('Company'),
'fieldtype': 'Link',
'options': 'Company',
'width': '120'
},
{
'fieldname': 'patient',
'label': _('Patient'),
'fieldtype': 'Link',
'options': 'Patient',
'width': '120'
},
{
'fieldname': 'patient_name',
'label': _('Patient Name'),
'fieldtype': 'Data',
'width': '120'
},
{
'fieldname': 'employee',
'label': _('Lab Technician'),
'fieldtype': 'Link',
'options': 'Employee',
'width': '120'
},
{
'fieldname': 'status',
'label': _('Status'),
'fieldtype': 'Data',
'width': '100'
},
{
'fieldname': 'invoiced',
'label': _('Invoiced'),
'fieldtype': 'Check',
'width': '100'
},
{
'fieldname': 'result_date',
'label': _('Result Date'),
'fieldtype': 'Date',
'width': '100'
},
{
'fieldname': 'practitioner',
'label': _('Requesting Practitioner'),
'fieldtype': 'Link',
'options': 'Healthcare Practitioner',
'width': '120'
},
{
'fieldname': 'department',
'label': _('Medical Department'),
'fieldtype': 'Link',
'options': 'Medical Department',
'width': '100'
}
]
return columns
def get_lab_tests(filters):
conditions = get_conditions(filters)
data = frappe.get_all(
doctype='Lab Test',
fields=['name', 'template', 'company', 'patient', 'patient_name', 'practitioner', 'employee', 'status', 'invoiced', 'result_date', 'department'],
filters=conditions,
order_by='submitted_date desc'
)
return data
def get_conditions(filters):
conditions = ""
conditions = {
'docstatus': ('=', 1)
}
if filters.get("patient"):
conditions += "and patient = %(patient)s"
if filters.get("from_date"):
conditions += "and result_date >= %(from_date)s"
if filters.get("to_date"):
conditions += " and result_date <= %(to_date)s"
if filters.get("department"):
conditions += " and department = %(department)s"
if filters.get('from_date') and filters.get('to_date'):
conditions['result_date'] = ('between', (filters.get('from_date'), filters.get('to_date')))
filters.pop('from_date')
filters.pop('to_date')
for key, value in filters.items():
if filters.get(key):
conditions[key] = value
return conditions
def get_lab_test(filters):
conditions = get_conditions(filters)
return frappe.db.sql("""select name, patient, lab_test_name, patient_name, status, result_date, practitioner, invoiced, department
from `tabLab Test`
where docstatus<2 %s order by submitted_date desc, name desc""" %
conditions, filters, as_dict=1)
def get_chart_data(data):
if not data:
return None
labels = ['Completed', 'Approved', 'Rejected']
status_wise_data = {
'Completed': 0,
'Approved': 0,
'Rejected': 0
}
datasets = []
for entry in data:
status_wise_data[entry.status] += 1
datasets.append({
'name': 'Lab Test Status',
'values': [status_wise_data.get('Completed'), status_wise_data.get('Approved'), status_wise_data.get('Rejected')]
})
chart = {
'data': {
'labels': labels,
'datasets': datasets
},
'type': 'donut',
'height': 300,
}
return chart
def get_report_summary(data):
if not data:
return None
total_lab_tests = len(data)
invoiced_lab_tests, unbilled_lab_tests = 0, 0
for entry in data:
if entry.invoiced:
invoiced_lab_tests += 1
else:
unbilled_lab_tests += 1
return [
{
'value': total_lab_tests,
'indicator': 'Blue',
'label': 'Total Lab Tests',
'datatype': 'Int',
},
{
'value': invoiced_lab_tests,
'indicator': 'Green',
'label': 'Invoiced Lab Tests',
'datatype': 'Int',
},
{
'value': unbilled_lab_tests,
'indicator': 'Red',
'label': 'Unbilled Lab Tests',
'datatype': 'Int',
}
]

View File

@ -282,6 +282,11 @@ auto_cancel_exempted_doctypes= [
]
scheduler_events = {
"cron": {
"0/30 * * * *": [
"erpnext.utilities.doctype.video.video.update_youtube_data",
]
},
"all": [
"erpnext.projects.doctype.project.project.project_status_update_reminder",
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder",

View File

@ -696,11 +696,11 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-01-30 11:28:08.401459",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal",
"owner": "ashwini@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"amend": 0,

View File

@ -207,11 +207,11 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-07-11 03:27:57.897071",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal Goal",
"owner": "ashwini@webnotestech.com",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,

View File

@ -113,11 +113,11 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-13 12:37:56.937023",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal Template",
"owner": "ashwini@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"amend": 0,

View File

@ -78,11 +78,11 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-07-11 03:27:57.979215",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal Template Goal",
"owner": "ashwini@webnotestech.com",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,

View File

@ -205,11 +205,11 @@
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-05-29 13:51:37.177231",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance",
"owner": "ashwini@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"cancel": 1,

View File

@ -371,12 +371,12 @@
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-06-15 12:43:04.099803",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim",
"name_case": "Title Case",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"amend": 1,

View File

@ -120,11 +120,11 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2020-05-11 18:54:35.601592",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Detail",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"

View File

@ -48,11 +48,11 @@
"icon": "fa fa-flag",
"idx": 1,
"links": [],
"modified": "2019-12-11 13:38:59.534034",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Type",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"create": 1,

View File

@ -229,11 +229,11 @@
"issingle": 1,
"istable": 0,
"max_attachments": 1,
"modified": "2017-11-14 12:51:34.980103",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "HR",
"name": "Upload Attendance",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"amend": 0,

View File

@ -40,8 +40,8 @@
"mapping_name": "Company to Hub Company",
"mapping_type": "Push",
"migration_id_field": "hub_sync_id",
"modified": "2018-02-14 15:57:05.571142",
"modified_by": "achilles@erpnext.com",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"name": "Company to Hub Company",
"owner": "Administrator",
"page_length": 10,

View File

@ -21,10 +21,10 @@
"mapping_name": "Hub Message to Lead",
"mapping_type": "Pull",
"migration_id_field": "hub_sync_id",
"modified": "2018-02-14 15:57:05.606597",
"modified_by": "achilles@erpnext.com",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"name": "Hub Message to Lead",
"owner": "frappetest@gmail.com",
"owner": "Administrator",
"page_length": 10,
"remote_objectname": "Hub Message",
"remote_primary_key": "name"

View File

@ -121,8 +121,8 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-09-01 13:56:07.816894",
"modified_by": "netchamp@rawcoderz.com",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub User",
"name_case": "",

View File

@ -54,12 +54,12 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-03-06 04:41:17.916243",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Users",
"name_case": "",
"owner": "test1@example.com",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,

View File

@ -352,12 +352,12 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2019-02-01 14:21:16.729848",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Marketplace Settings",
"name_case": "",
"owner": "netchamp@rawcoderz.com",
"owner": "Administrator",
"permissions": [
{
"amend": 0,

View File

@ -813,7 +813,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 14:44:33.670332",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Schedule",

View File

@ -1001,11 +1001,11 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2020-07-15 14:44:44.911402",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Visit",
"owner": "ashwini@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"amend": 1,

View File

@ -125,11 +125,11 @@
],
"idx": 1,
"istable": 1,
"modified": "2019-10-03 14:55:52.786805",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Visit Purpose",
"owner": "ashwini@webnotestech.com",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",

View File

@ -1,336 +1,105 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:email",
"beta": 0,
"creation": "2017-09-19 16:20:27.510196",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_rename": 1,
"autoname": "field:email",
"creation": "2017-09-19 16:20:27.510196",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"donor_name",
"column_break_5",
"donor_type",
"email",
"image",
"address_contacts",
"address_html",
"column_break_9",
"contact_html"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "donor_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Donor Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "donor_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Donor Name",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "donor_type",
"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": "Donor Type",
"length": 0,
"no_copy": 0,
"options": "Donor Type",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "donor_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Donor Type",
"options": "Donor Type",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "email",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Email",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "email",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Email",
"reqd": 1,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "image",
"fieldtype": "Attach Image",
"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": "Image",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
"label": "Image",
"no_copy": 1,
"print_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "address_contacts",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Address and Contact",
"length": 0,
"no_copy": 0,
"options": "fa fa-map-marker",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"depends_on": "eval:!doc.__islocal;",
"fieldname": "address_contacts",
"fieldtype": "Section Break",
"label": "Address and Contact",
"options": "fa fa-map-marker"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "address_html",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Address HTML",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "address_html",
"fieldtype": "HTML",
"label": "Address HTML"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "contact_html",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Contact HTML",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"fieldname": "contact_html",
"fieldtype": "HTML",
"label": "Contact HTML"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_field": "image",
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-01-22 15:53:35.059946",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Donor",
"name_case": "",
"owner": "Administrator",
],
"image_field": "image",
"links": [],
"modified": "2020-09-16 23:46:04.083274",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Donor",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Non Profit Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Non Profit Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Non Profit",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "donor_name",
"track_changes": 1,
"track_seen": 0
],
"quick_entry": 1,
"restrict_to_domain": "Non Profit",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "donor_name",
"track_changes": 1
}

View File

@ -111,6 +111,7 @@
"options": "Supplier"
},
{
"depends_on": "eval:!doc.__islocal;",
"fieldname": "address_contacts",
"fieldtype": "Section Break",
"label": "Address and Contact",
@ -177,7 +178,7 @@
],
"image_field": "image",
"links": [],
"modified": "2020-08-06 10:06:01.153564",
"modified": "2020-09-16 23:44:13.596948",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Member",

View File

@ -90,9 +90,9 @@
},
{
"fieldname": "currency",
"fieldtype": "Select",
"fieldtype": "Link",
"label": "Currency",
"options": "USD\nINR"
"options": "Currency"
},
{
"fieldname": "amount",
@ -126,7 +126,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2020-07-31 13:57:02.328995",
"modified": "2020-09-19 14:28:11.532696",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Membership",
@ -161,4 +161,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@ -1,580 +1,148 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:email",
"beta": 0,
"creation": "2017-09-19 16:16:45.676019",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"allow_rename": 1,
"autoname": "field:email",
"creation": "2017-09-19 16:16:45.676019",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"volunteer_name",
"column_break_5",
"volunteer_type",
"email",
"image",
"address_contacts",
"address_html",
"column_break_9",
"contact_html",
"volunteer_availability_and_skills_details",
"availability",
"availability_timeslot",
"column_break_12",
"volunteer_skills",
"section_break_15",
"note"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "volunteer_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Volunteer Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "volunteer_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Volunteer Name",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 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,
"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
},
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "volunteer_type",
"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": "Volunteer Type",
"length": 0,
"no_copy": 0,
"options": "Volunteer Type",
"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
},
"fieldname": "volunteer_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Volunteer Type",
"options": "Volunteer Type",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "email",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Email",
"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,
"fieldname": "email",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Email",
"reqd": 1,
"unique": 1
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "image",
"fieldtype": "Attach Image",
"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": "Image",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"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
},
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
"label": "Image",
"no_copy": 1,
"print_hide": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "address_contacts",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Address and Contact",
"length": 0,
"no_copy": 0,
"options": "fa fa-map-marker",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"depends_on": "eval:!doc.__islocal;",
"fieldname": "address_contacts",
"fieldtype": "Section Break",
"label": "Address and Contact",
"options": "fa fa-map-marker"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "address_html",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Address HTML",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "address_html",
"fieldtype": "HTML",
"label": "Address HTML"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_9",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "contact_html",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Contact HTML",
"length": 0,
"no_copy": 0,
"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
},
"fieldname": "contact_html",
"fieldtype": "HTML",
"label": "Contact HTML"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "volunteer_availability_and_skills_details",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Availability and Skills",
"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
},
"fieldname": "volunteer_availability_and_skills_details",
"fieldtype": "Section Break",
"label": "Availability and Skills"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "availability",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Availability",
"length": 0,
"no_copy": 0,
"options": "\nWeekly\nWeekdays\nWeekends",
"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
},
"fieldname": "availability",
"fieldtype": "Select",
"label": "Availability",
"options": "\nWeekly\nWeekdays\nWeekends"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "availability_timeslot",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Availability Timeslot",
"length": 0,
"no_copy": 0,
"options": "\nMorning\nAfternoon\nEvening\nAnytime",
"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
},
"fieldname": "availability_timeslot",
"fieldtype": "Select",
"label": "Availability Timeslot",
"options": "\nMorning\nAfternoon\nEvening\nAnytime"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_12",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "volunteer_skills",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Volunteer Skills",
"length": 0,
"no_copy": 0,
"options": "Volunteer Skill",
"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
},
"fieldname": "volunteer_skills",
"fieldtype": "Table",
"label": "Volunteer Skills",
"options": "Volunteer Skill"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_15",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "section_break_15",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "note",
"fieldtype": "Long Text",
"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": "Note",
"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
"fieldname": "note",
"fieldtype": "Long Text",
"label": "Note"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_field": "image",
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-11-04 03:36:25.776211",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Volunteer",
"name_case": "",
"owner": "Administrator",
],
"image_field": "image",
"links": [],
"modified": "2020-09-16 23:45:15.595952",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Volunteer",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Non Profit Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Non Profit Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Non Profit",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "volunteer_name",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
],
"quick_entry": 1,
"restrict_to_domain": "Non Profit",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "volunteer_name",
"track_changes": 1
}

Some files were not shown because too many files have changed in this diff Show More