Merge branch 'develop' into supplier-quotn-comparison

This commit is contained in:
Marica 2020-09-21 22:08:38 +05:30 committed by GitHub
commit cbac025a5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 465 additions and 596 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

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

@ -45,11 +45,11 @@
],
"icon": "fa fa-credit-card",
"idx": 1,
"modified": "2019-08-14 14:58:42.079115",
"modified_by": "sammish.thundiyil@gmail.com",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Mode of Payment",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"create": 1,

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

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

@ -7,10 +7,10 @@ frappe.ui.form.on('POS Settings', {
},
get_invoice_fields: function(frm) {
frappe.model.with_doctype("Sales Invoice", () => {
var fields = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
frappe.model.with_doctype("POS Invoice", () => {
var fields = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
d.fieldtype === 'Table') {
['Table', 'Button'].includes(d.fieldtype)) {
return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
} else {
return null;
@ -25,7 +25,7 @@ frappe.ui.form.on('POS Settings', {
frappe.ui.form.on("POS Field", {
fieldname: function(frm, doctype, name) {
var doc = frappe.get_doc(doctype, name);
var df = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
var df = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
return doc.fieldname == d.fieldname ? d : null;
})[0];

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

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

@ -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
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")
@ -294,7 +299,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

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

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

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

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

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

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

@ -4,17 +4,16 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc("erpnext_integrations", "doctype", "plaid_settings")
plaid_settings = frappe.get_single("Plaid Settings")
if plaid_settings.enabled:
if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env \
and frappe.conf.plaid_public_key and frappe.conf.plaid_secret):
if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env and frappe.conf.plaid_secret):
plaid_settings.enabled = 0
else:
plaid_settings.update({
"plaid_client_id": frappe.conf.plaid_client_id,
"plaid_public_key": frappe.conf.plaid_public_key,
"plaid_env": frappe.conf.plaid_env,
"plaid_secret": frappe.conf.plaid_secret
})

View File

@ -48,8 +48,8 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-10-26 17:37:25.638190",
"modified_by": "rohitw1991@gmail.com",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Projects",
"name": "Dependent Task",
"name_case": "",

View File

@ -11,7 +11,9 @@ $.extend(frappe.breadcrumbs.preferred, {
"Territory": "Selling",
"Sales Person": "Selling",
"Sales Partner": "Selling",
"Brand": "Stock"
"Brand": "Stock",
"Maintenance Schedule": "Support",
"Maintenance Visit": "Support"
});
$.extend(frappe.breadcrumbs.module_map, {

View File

@ -224,6 +224,10 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) {
party = frm.doc.party_name;
}
if (!frm.doc.company) {
frappe.throw(__("Kindly select the company first"));
}
frappe.call({
method: "erpnext.accounts.party.set_taxes",
args: {
@ -292,4 +296,4 @@ erpnext.utils.get_shipping_address = function(frm, callback){
} else {
frappe.msgprint(__("Select company first"));
}
}
}

View File

@ -49,11 +49,11 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-07-25 05:24:25.881361",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Selling",
"name": "Industry Type",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"amend": 0,

View File

@ -238,8 +238,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-18 14:23:06.538568",
"modified_by": "tundebabzy@gmail.com",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Selling",
"name": "Product Bundle",
"owner": "Administrator",

View File

@ -70,13 +70,23 @@ erpnext.PointOfSale.Payment = class {
this.$invoice_fields.append(
`<div class="invoice_detail_field ${df.fieldname}-field" data-fieldname="${df.fieldname}"></div>`
);
let df_events = {
onchange: function() { frm.set_value(this.df.fieldname, this.value); }
}
if (df.fieldtype == "Button") {
df_events = {
click: function() {
if (frm.script_manager.has_handlers(df.fieldname, frm.doc.doctype)) {
frm.script_manager.trigger(df.fieldname, frm.doc.doctype, frm.doc.docname);
}
}
}
}
this[`${df.fieldname}_field`] = frappe.ui.form.make_control({
df: {
...df,
onchange: function() {
frm.set_value(this.df.fieldname, this.value);
}
...df_events
},
parent: this.$invoice_fields.find(`.${df.fieldname}-field`),
render_input: true,

View File

@ -188,7 +188,7 @@ def get_conditions(filters):
conditions += "AND so.transaction_date <= '%s'" %filters.to_date
if filters.get("item_code"):
conditions += "AND so_item.item_code = '%s'" %frappe.db.escape(filters.item_code)
conditions += "AND so_item.item_code = %s" %frappe.db.escape(filters.item_code)
if filters.get("customer"):
conditions += "AND so.customer = %s" %frappe.db.escape(filters.customer)

View File

@ -166,11 +166,11 @@
"idx": 1,
"image_field": "image",
"max_attachments": 5,
"modified": "2019-10-03 22:38:45.104056",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Stock",
"name": "Batch",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"create": 1,

View File

@ -133,11 +133,11 @@
],
"idx": 1,
"istable": 1,
"modified": "2019-11-12 15:41:21.053462",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Item",
"owner": "wasim@webnotestech.com",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"

View File

@ -173,11 +173,11 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-07-20 10:49:34.228751",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Purchase Receipt",
"owner": "wasim@webnotestech.com",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,

View File

@ -494,8 +494,7 @@ class TestPurchaseReceipt(unittest.TestCase):
"expected_value_after_useful_life": 10,
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 1,
"depreciation_start_date": frappe.utils.nowdate()
"frequency_of_depreciation": 1
})
asset.submit()

View File

@ -23,6 +23,24 @@ frappe.ui.form.on('Stock Entry', {
}
});
frm.set_query('source_warehouse_address', function() {
return {
filters: {
link_doctype: 'Warehouse',
link_name: frm.doc.from_warehouse
}
}
});
frm.set_query('target_warehouse_address', function() {
return {
filters: {
link_doctype: 'Warehouse',
link_name: frm.doc.to_warehouse
}
}
});
frappe.db.get_value('Stock Settings', {name: 'Stock Settings'}, 'sample_retention_warehouse', (r) => {
if (r.sample_retention_warehouse) {
var filters = [

View File

@ -361,11 +361,11 @@
],
"icon": "fa fa-bug",
"idx": 1,
"modified": "2019-05-24 10:56:30.626200",
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Support",
"name": "Warranty Claim",
"owner": "harshada@webnotestech.com",
"owner": "Administrator",
"permissions": [
{
"create": 1,

View File

@ -11,8 +11,39 @@ def get_level():
activation_level = 0
sales_data = []
min_count = 0
doctypes = {"Item": 5, "Customer": 5, "Sales Order": 2, "Sales Invoice": 2, "Purchase Order": 2, "Employee": 3, "Lead": 3, "Quotation": 3,
"Payment Entry": 2, "User": 5, "Student": 5, "Instructor": 5, "BOM": 3, "Journal Entry": 3, "Stock Entry": 3}
doctypes = {
"Asset": 5,
"BOM": 3,
"Customer": 5,
"Delivery Note": 5,
"Employee": 3,
"Instructor": 5,
"Instructor": 5,
"Issue": 5,
"Item": 5,
"Journal Entry": 3,
"Lead": 3,
"Leave Application": 5,
"Material Request": 5,
"Opportunity": 5,
"Payment Entry": 2,
"Project": 5,
"Purchase Order": 2,
"Purchase Invoice": 5,
"Purchase Receipt": 5,
"Quotation": 3,
"Salary Slip": 5,
"Salary Structure": 5,
"Sales Order": 2,
"Sales Invoice": 2,
"Stock Entry": 3,
"Student": 5,
"Supplier": 5,
"Task": 5,
"User": 5,
"Work Order": 5
}
for doctype, min_count in iteritems(doctypes):
count = frappe.db.count(doctype)
if count > min_count:

View File

@ -3,7 +3,7 @@ frappe
gocardless-pro==1.11.0
googlemaps==3.1.1
pandas==1.0.5
plaid-python==3.4.0
plaid-python==6.0.0
pycountry==19.8.18
PyGithub==1.44.1
python-stdnum==1.12