Merge branch 'develop' into supplier-quotn-comparison

This commit is contained in:
Marica
2020-09-21 22:08:38 +05:30
committed by GitHub
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: ERPNext as a monolith includes the following areas for managing businesses:
1. [Accounting](https://erpnext.com/open-source-accounting) 1. [Accounting](https://erpnext.com/open-source-accounting)
1. [Inventory](https://erpnext.com/distribution/inventory-management-system) 1. [Warehouse Management](https://erpnext.com/distribution/warehouse-management-system)
1. [CRM](https://erpnext.com/open-source-crm) 1. [CRM](https://erpnext.com/open-source-crm)
1. [Sales](https://erpnext.com/open-source-sales-purchase) 1. [Sales](https://erpnext.com/open-source-sales-purchase)
1. [Purchase](https://erpnext.com/open-source-sales-purchase) 1. [Purchase](https://erpnext.com/open-source-sales-purchase)

View File

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

View File

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

View File

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

View File

@@ -5,13 +5,14 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cint from frappe.utils import cint, get_link_to_form
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.controllers.status_updater import StatusUpdater from erpnext.controllers.status_updater import StatusUpdater
class POSOpeningEntry(StatusUpdater): class POSOpeningEntry(StatusUpdater):
def validate(self): def validate(self):
self.validate_pos_profile_and_cashier() self.validate_pos_profile_and_cashier()
self.validate_payment_method_account()
self.set_status() self.set_status()
def validate_pos_profile_and_cashier(self): def validate_pos_profile_and_cashier(self):
@@ -21,5 +22,13 @@ class POSOpeningEntry(StatusUpdater):
if not cint(frappe.db.get_value("User", self.user, "enabled")): if not cint(frappe.db.get_value("User", self.user, "enabled")):
frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user))) frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user)))
def validate_payment_method_account(self):
for d in self.balance_details:
account = frappe.db.get_value("Mode of Payment Account",
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
if not account:
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
def on_submit(self): def on_submit(self):
self.set_status(update=True) self.set_status(update=True)

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import msgprint, _ from frappe import msgprint, _
from frappe.utils import cint, now from frappe.utils import cint, now, get_link_to_form
from six import iteritems from six import iteritems
from frappe.model.document import Document from frappe.model.document import Document
@@ -13,7 +13,7 @@ class POSProfile(Document):
self.validate_default_profile() self.validate_default_profile()
self.validate_all_link_fields() self.validate_all_link_fields()
self.validate_duplicate_groups() self.validate_duplicate_groups()
self.check_default_payment() self.validate_payment_methods()
def validate_default_profile(self): def validate_default_profile(self):
for row in self.applicable_for_users: for row in self.applicable_for_users:
@@ -52,14 +52,23 @@ class POSProfile(Document):
if len(customer_groups) != len(set(customer_groups)): if len(customer_groups) != len(set(customer_groups)):
frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group") frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group")
def check_default_payment(self): def validate_payment_methods(self):
if self.payments: if not self.payments:
default_mode_of_payment = [d.default for d in self.payments if d.default] frappe.throw(_("Payment methods are mandatory. Please add at least one payment method."))
if not default_mode_of_payment:
frappe.throw(_("Set default mode of payment"))
if len(default_mode_of_payment) > 1: default_mode_of_payment = [d.default for d in self.payments if d.default]
frappe.throw(_("Multiple default mode of payment is not allowed")) if not default_mode_of_payment:
frappe.throw(_("Please select a default mode of payment"))
if len(default_mode_of_payment) > 1:
frappe.throw(_("You can only select one mode of payment as default"))
for d in self.payments:
account = frappe.db.get_value("Mode of Payment Account",
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
if not account:
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
def on_update(self): def on_update(self):
self.set_defaults() self.set_defaults()

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
import frappe.defaults import frappe.defaults
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate, get_link_to_form
from frappe import _, msgprint, throw from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date from erpnext.accounts.party import get_party_account, get_due_date
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
@@ -1372,7 +1372,7 @@ def get_bank_cash_account(mode_of_payment, company):
{"parent": mode_of_payment, "company": company}, "default_account") {"parent": mode_of_payment, "company": company}, "default_account")
if not account: if not account:
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}") frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
.format(mode_of_payment)) .format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
return { return {
"account": account "account": account
} }
@@ -1612,18 +1612,16 @@ def update_multi_mode_option(doc, pos_profile):
payment.type = payment_mode.type payment.type = payment_mode.type
doc.set('payments', []) doc.set('payments', [])
if not pos_profile or not pos_profile.get('payments'):
for payment_mode in get_all_mode_of_payments(doc):
append_payment(payment_mode)
return
for pos_payment_method in pos_profile.get('payments'): for pos_payment_method in pos_profile.get('payments'):
pos_payment_method = pos_payment_method.as_dict() pos_payment_method = pos_payment_method.as_dict()
payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company) payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
if payment_mode: if not payment_mode:
payment_mode[0].default = pos_payment_method.default frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
append_payment(payment_mode[0]) .format(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment)), title=_("Missing Account"))
payment_mode[0].default = pos_payment_method.default
append_payment(payment_mode[0])
def get_all_mode_of_payments(doc): def get_all_mode_of_payments(doc):
return frappe.db.sql(""" return frappe.db.sql("""

View File

@@ -72,7 +72,7 @@ erpnext.accounts.bankReconciliation = class BankReconciliation {
check_plaid_status() { check_plaid_status() {
const me = this; const me = this;
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => { frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
if (r && r.enabled == "1") { if (r && r.enabled === "1") {
me.plaid_status = "active" me.plaid_status = "active"
} else { } else {
me.plaid_status = "inactive" me.plaid_status = "inactive"
@@ -214,31 +214,35 @@ erpnext.accounts.bankTransactionSync = class bankTransactionSync {
init_config() { init_config() {
const me = this; const me = this;
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration') frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration')
.then(result => { .then(result => {
me.plaid_env = result.plaid_env; me.plaid_env = result.plaid_env;
me.plaid_public_key = result.plaid_public_key; me.client_name = result.client_name;
me.client_name = result.client_name; me.link_token = result.link_token;
me.sync_transactions() me.sync_transactions();
}) })
} }
sync_transactions() { sync_transactions() {
const me = this; const me = this;
frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (v) => { frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => {
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', { frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
bank: v['bank'], bank: r.bank,
bank_account: me.parent.bank_account, bank_account: me.parent.bank_account,
freeze: true freeze: true
}) })
.then((result) => { .then((result) => {
let result_title = (result.length > 0) ? __("{0} bank transaction(s) created", [result.length]) : __("This bank account is already synchronized") let result_title = (result && result.length > 0)
? __("{0} bank transaction(s) created", [result.length])
: __("This bank account is already synchronized");
let result_msg = ` let result_msg = `
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;"> <div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
<h5 class="text-muted">${result_title}</h5> <h5 class="text-muted">${result_title}</h5>
</div>` </div>`
this.parent.$main_section.append(result_msg) this.parent.$main_section.append(result_msg)
frappe.show_alert({message:__("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator:'green'}); frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' });
}) })
}) })
} }
@@ -384,7 +388,7 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
}) })
frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments', frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
{bank_transaction: data, freeze:true, freeze_message:__("Finding linked payments")} { bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") }
).then((result) => { ).then((result) => {
me.make_dialog(result) me.make_dialog(result)
}) })

View File

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

View File

@@ -617,9 +617,19 @@ class ReceivablePayableReport(object):
elif party_type_field=="supplier": elif party_type_field=="supplier":
self.add_supplier_filters(conditions, values) self.add_supplier_filters(conditions, values)
if self.filters.cost_center:
self.get_cost_center_conditions(conditions)
self.add_accounting_dimensions_filters(conditions, values) self.add_accounting_dimensions_filters(conditions, values)
return " and ".join(conditions), values return " and ".join(conditions), values
def get_cost_center_conditions(self, conditions):
lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"])
cost_center_list = [center.name for center in frappe.get_list("Cost Center", filters = {'lft': (">=", lft), 'rgt': ("<=", rgt)})]
cost_center_string = '", "'.join(cost_center_list)
conditions.append('cost_center in ("{0}")'.format(cost_center_string))
def get_order_by_condition(self): def get_order_by_condition(self):
if self.filters.get('group_by_party'): if self.filters.get('group_by_party'):
return "order by party, posting_date" return "order by party, posting_date"

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) { is_existing_asset: function(frm) {
frm.trigger("toggle_reference_doc"); frm.trigger("toggle_reference_doc");
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation)); // frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
@@ -438,6 +431,15 @@ frappe.ui.form.on('Asset Finance Book', {
} }
frappe.flags.dont_change_rate = false; frappe.flags.dont_change_rate = false;
},
depreciation_start_date: function(frm, cdt, cdn) {
const book = locals[cdt][cdn];
if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) {
frappe.msgprint(__(`Depreciation Posting Date should not be equal to Available for Use Date.`));
book.depreciation_start_date = "";
frm.refresh_field("finance_books");
}
} }
}); });

View File

@@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, erpnext, math, json import frappe, erpnext, math, json
from frappe import _ from frappe import _
from six import string_types from six import string_types
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days, get_last_day
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.assets.doctype.asset.depreciation \ from erpnext.assets.doctype.asset.depreciation \
@@ -83,6 +83,11 @@ class Asset(AccountsController):
if not self.available_for_use_date: if not self.available_for_use_date:
frappe.throw(_("Available for use date is required")) frappe.throw(_("Available for use date is required"))
for d in self.finance_books:
if d.depreciation_start_date == self.available_for_use_date:
frappe.throw(_("Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.").format(d.idx),
title=_("Incorrect Date"))
def set_missing_values(self): def set_missing_values(self):
if not self.asset_category: if not self.asset_category:
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category") self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
@@ -294,7 +299,7 @@ class Asset(AccountsController):
if not row.depreciation_start_date: if not row.depreciation_start_date:
if not self.available_for_use_date: if not self.available_for_use_date:
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx)) frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
row.depreciation_start_date = self.available_for_use_date row.depreciation_start_date = get_last_day(self.available_for_use_date)
if not self.is_existing_asset: if not self.is_existing_asset:
self.opening_accumulated_depreciation = 0 self.opening_accumulated_depreciation = 0

View File

@@ -371,19 +371,18 @@ class TestAsset(unittest.TestCase):
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name) asset = frappe.get_doc('Asset', asset_name)
asset.calculate_depreciation = 1 asset.calculate_depreciation = 1
asset.available_for_use_date = nowdate() asset.available_for_use_date = '2020-01-01'
asset.purchase_date = nowdate() asset.purchase_date = '2020-01-01'
asset.append("finance_books", { asset.append("finance_books", {
"expected_value_after_useful_life": 10000, "expected_value_after_useful_life": 10000,
"depreciation_method": "Straight Line", "depreciation_method": "Straight Line",
"total_number_of_depreciations": 3, "total_number_of_depreciations": 10,
"frequency_of_depreciation": 10, "frequency_of_depreciation": 1
"depreciation_start_date": nowdate()
}) })
asset.insert() asset.insert()
asset.submit() asset.submit()
post_depreciation_entries(date=add_months(nowdate(), 10)) post_depreciation_entries(date=add_months('2020-01-01', 4))
scrap_asset(asset.name) scrap_asset(asset.name)
@@ -392,9 +391,9 @@ class TestAsset(unittest.TestCase):
self.assertTrue(asset.journal_entry_for_scrap) self.assertTrue(asset.journal_entry_for_scrap)
expected_gle = ( expected_gle = (
("_Test Accumulated Depreciations - _TC", 30000.0, 0.0), ("_Test Accumulated Depreciations - _TC", 36000.0, 0.0),
("_Test Fixed Asset - _TC", 0.0, 100000.0), ("_Test Fixed Asset - _TC", 0.0, 100000.0),
("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0) ("_Test Gain/Loss on Asset Disposal - _TC", 64000.0, 0.0)
) )
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
@@ -466,8 +465,7 @@ class TestAsset(unittest.TestCase):
"expected_value_after_useful_life": 10000, "expected_value_after_useful_life": 10000,
"depreciation_method": "Straight Line", "depreciation_method": "Straight Line",
"total_number_of_depreciations": 3, "total_number_of_depreciations": 3,
"frequency_of_depreciation": 10, "frequency_of_depreciation": 10
"depreciation_start_date": "2020-06-06"
}) })
asset.insert() asset.insert()
accumulated_depreciation_after_full_schedule = \ accumulated_depreciation_after_full_schedule = \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,81 +2,90 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals import plaid
from frappe import _ import requests
from frappe.utils.password import get_decrypted_password from plaid.errors import APIError, ItemError, InvalidRequestError
from plaid import Client
from plaid.errors import APIError, ItemError
import frappe import frappe
import requests from frappe import _
class PlaidConnector(): class PlaidConnector():
def __init__(self, access_token=None): def __init__(self, access_token=None):
plaid_settings = frappe.get_single("Plaid Settings")
self.config = {
"plaid_client_id": plaid_settings.plaid_client_id,
"plaid_secret": get_decrypted_password("Plaid Settings", "Plaid Settings", 'plaid_secret'),
"plaid_public_key": plaid_settings.plaid_public_key,
"plaid_env": plaid_settings.plaid_env
}
self.client = Client(client_id=self.config.get("plaid_client_id"),
secret=self.config.get("plaid_secret"),
public_key=self.config.get("plaid_public_key"),
environment=self.config.get("plaid_env")
)
self.access_token = access_token self.access_token = access_token
self.settings = frappe.get_single("Plaid Settings")
self.products = ["auth", "transactions"]
self.client_name = frappe.local.site
self.client = plaid.Client(
client_id=self.settings.plaid_client_id,
secret=self.settings.get_password("plaid_secret"),
environment=self.settings.plaid_env,
api_version="2019-05-29"
)
def get_access_token(self, public_token): def get_access_token(self, public_token):
if public_token is None: if public_token is None:
frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error")) frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error"))
response = self.client.Item.public_token.exchange(public_token) response = self.client.Item.public_token.exchange(public_token)
access_token = response['access_token'] access_token = response["access_token"]
return access_token return access_token
def get_link_token(self):
token_request = {
"client_name": self.client_name,
"client_id": self.settings.plaid_client_id,
"secret": self.settings.plaid_secret,
"products": self.products,
# only allow Plaid-supported languages and countries (LAST: Sep-19-2020)
"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
"country_codes": ["US", "CA", "FR", "IE", "NL", "ES", "GB"],
"user": {
"client_user_id": frappe.generate_hash(frappe.session.user, length=32)
}
}
try:
response = self.client.LinkToken.create(token_request)
except InvalidRequestError:
frappe.log_error(frappe.get_traceback(), _("Plaid invalid request error"))
frappe.msgprint(_("Please check your Plaid client ID and secret values"))
except APIError as e:
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
frappe.throw(_(str(e)), title=_("Authentication Failed"))
else:
return response["link_token"]
def auth(self): def auth(self):
try: try:
self.client.Auth.get(self.access_token) self.client.Auth.get(self.access_token)
print("Authentication successful.....")
except ItemError as e: except ItemError as e:
if e.code == 'ITEM_LOGIN_REQUIRED': if e.code == "ITEM_LOGIN_REQUIRED":
pass
else:
pass pass
except APIError as e: except APIError as e:
if e.code == 'PLANNED_MAINTENANCE': if e.code == "PLANNED_MAINTENANCE":
pass
else:
pass pass
except requests.Timeout: except requests.Timeout:
pass pass
except Exception as e: except Exception as e:
print(e)
frappe.log_error(frappe.get_traceback(), _("Plaid authentication error")) frappe.log_error(frappe.get_traceback(), _("Plaid authentication error"))
frappe.msgprint({"title": _("Authentication Failed"), "message":e, "raise_exception":1, "indicator":'red'}) frappe.throw(_(str(e)), title=_("Authentication Failed"))
def get_transactions(self, start_date, end_date, account_id=None): def get_transactions(self, start_date, end_date, account_id=None):
self.auth()
kwargs = dict(
access_token=self.access_token,
start_date=start_date,
end_date=end_date
)
if account_id:
kwargs.update(dict(account_ids=[account_id]))
try: try:
self.auth() response = self.client.Transactions.get(**kwargs)
if account_id: transactions = response["transactions"]
account_ids = [account_id] while len(transactions) < response["total_transactions"]:
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, account_ids=account_ids)
else:
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date)
transactions = response['transactions']
while len(transactions) < response['total_transactions']:
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions)) response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
transactions.extend(response['transactions']) transactions.extend(response["transactions"])
return transactions return transactions
except Exception: except Exception:
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))

View File

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

View File

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

View File

@@ -2,30 +2,36 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import json import json
from frappe import _
from frappe.model.document import Document import frappe
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector
from frappe.utils import getdate, formatdate, today, add_months from frappe import _
from frappe.desk.doctype.tag.tag import add_tag from frappe.desk.doctype.tag.tag import add_tag
from frappe.model.document import Document
from frappe.utils import add_months, formatdate, getdate, today
class PlaidSettings(Document): class PlaidSettings(Document):
pass @staticmethod
def get_link_token():
plaid = PlaidConnector()
return plaid.get_link_token()
@frappe.whitelist() @frappe.whitelist()
def plaid_configuration(): def get_plaid_configuration():
if frappe.db.get_single_value("Plaid Settings", "enabled"): if frappe.db.get_single_value("Plaid Settings", "enabled"):
plaid_settings = frappe.get_single("Plaid Settings") plaid_settings = frappe.get_single("Plaid Settings")
return { return {
"plaid_public_key": plaid_settings.plaid_public_key,
"plaid_env": plaid_settings.plaid_env, "plaid_env": plaid_settings.plaid_env,
"link_token": plaid_settings.get_link_token(),
"client_name": frappe.local.site "client_name": frappe.local.site
} }
else:
return "disabled" return "disabled"
@frappe.whitelist() @frappe.whitelist()
def add_institution(token, response): def add_institution(token, response):
@@ -33,6 +39,7 @@ def add_institution(token, response):
plaid = PlaidConnector() plaid = PlaidConnector()
access_token = plaid.get_access_token(token) access_token = plaid.get_access_token(token)
bank = None
if not frappe.db.exists("Bank", response["institution"]["name"]): if not frappe.db.exists("Bank", response["institution"]["name"]):
try: try:
@@ -44,7 +51,6 @@ def add_institution(token, response):
bank.insert() bank.insert()
except Exception: except Exception:
frappe.throw(frappe.get_traceback()) frappe.throw(frappe.get_traceback())
else: else:
bank = frappe.get_doc("Bank", response["institution"]["name"]) bank = frappe.get_doc("Bank", response["institution"]["name"])
bank.plaid_access_token = access_token bank.plaid_access_token = access_token
@@ -52,6 +58,7 @@ def add_institution(token, response):
return bank return bank
@frappe.whitelist() @frappe.whitelist()
def add_bank_accounts(response, bank, company): def add_bank_accounts(response, bank, company):
try: try:
@@ -92,9 +99,8 @@ def add_bank_accounts(response, bank, company):
new_account.insert() new_account.insert()
result.append(new_account.name) result.append(new_account.name)
except frappe.UniqueValidationError: except frappe.UniqueValidationError:
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(new_account.account_name)) frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
except Exception: except Exception:
frappe.throw(frappe.get_traceback()) frappe.throw(frappe.get_traceback())
@@ -103,6 +109,7 @@ def add_bank_accounts(response, bank, company):
return result return result
def add_account_type(account_type): def add_account_type(account_type):
try: try:
frappe.get_doc({ frappe.get_doc({
@@ -122,10 +129,11 @@ def add_account_subtype(account_subtype):
except Exception: except Exception:
frappe.throw(frappe.get_traceback()) frappe.throw(frappe.get_traceback())
@frappe.whitelist() @frappe.whitelist()
def sync_transactions(bank, bank_account): def sync_transactions(bank, bank_account):
'''Sync transactions based on the last integration date as the start date, after the sync is completed """Sync transactions based on the last integration date as the start date, after sync is completed
add the transaction date of the oldest transaction as the last integration date''' add the transaction date of the oldest transaction as the last integration date."""
last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
if last_transaction_date: if last_transaction_date:
start_date = formatdate(last_transaction_date, "YYYY-MM-dd") start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
@@ -147,10 +155,10 @@ def sync_transactions(bank, bank_account):
len(result), bank_account, start_date, end_date)) len(result), bank_account, start_date, end_date))
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date) frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
except Exception: except Exception:
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
def get_transactions(bank, bank_account=None, start_date=None, end_date=None): def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
access_token = None access_token = None
@@ -168,6 +176,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
return transactions return transactions
def new_bank_transaction(transaction): def new_bank_transaction(transaction):
result = [] result = []
@@ -182,8 +191,8 @@ def new_bank_transaction(transaction):
status = "Pending" if transaction["pending"] == "True" else "Settled" status = "Pending" if transaction["pending"] == "True" else "Settled"
tags = []
try: try:
tags = []
tags += transaction["category"] tags += transaction["category"]
tags += ["Plaid Cat. {}".format(transaction["category_id"])] tags += ["Plaid Cat. {}".format(transaction["category_id"])]
except KeyError: except KeyError:
@@ -216,6 +225,7 @@ def new_bank_transaction(transaction):
return result return result
def automatic_synchronization(): def automatic_synchronization():
settings = frappe.get_doc("Plaid Settings", "Plaid Settings") settings = frappe.get_doc("Plaid Settings", "Plaid Settings")
@@ -223,4 +233,8 @@ def automatic_synchronization():
plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"]) plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"])
for plaid_account in plaid_accounts: for plaid_account in plaid_accounts:
frappe.enqueue("erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions", bank=plaid_account.bank, bank_account=plaid_account.name) frappe.enqueue(
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions",
bank=plaid_account.bank,
bank_account=plaid_account.name
)

View File

@@ -1,14 +1,17 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
from __future__ import unicode_literals
import unittest
import frappe
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import plaid_configuration, add_account_type, add_account_subtype, new_bank_transaction, add_bank_accounts
import json import json
from frappe.utils.response import json_handler import unittest
import frappe
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import (
add_account_subtype, add_account_type, add_bank_accounts,
new_bank_transaction, get_plaid_configuration)
from frappe.utils.response import json_handler
class TestPlaidSettings(unittest.TestCase): class TestPlaidSettings(unittest.TestCase):
def setUp(self): def setUp(self):
@@ -31,7 +34,7 @@ class TestPlaidSettings(unittest.TestCase):
def test_plaid_disabled(self): def test_plaid_disabled(self):
frappe.db.set_value("Plaid Settings", None, "enabled", 0) frappe.db.set_value("Plaid Settings", None, "enabled", 0)
self.assertTrue(plaid_configuration() == "disabled") self.assertTrue(get_plaid_configuration() == "disabled")
def test_add_account_type(self): def test_add_account_type(self):
add_account_type("brokerage") add_account_type("brokerage")
@@ -64,7 +67,7 @@ class TestPlaidSettings(unittest.TestCase):
'mask': '0000', 'mask': '0000',
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK', 'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
'name': 'Plaid Checking' 'name': 'Plaid Checking'
}], }],
'institution': { 'institution': {
'institution_id': 'ins_6', 'institution_id': 'ins_6',
'name': 'Citi' 'name': 'Citi'
@@ -100,7 +103,7 @@ class TestPlaidSettings(unittest.TestCase):
'mask': '0000', 'mask': '0000',
'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK', 'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK',
'name': 'Plaid Checking' 'name': 'Plaid Checking'
}], }],
'institution': { 'institution': {
'institution_id': 'ins_6', 'institution_id': 'ins_6',
'name': 'Citi' 'name': 'Citi'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,9 @@ $.extend(frappe.breadcrumbs.preferred, {
"Territory": "Selling", "Territory": "Selling",
"Sales Person": "Selling", "Sales Person": "Selling",
"Sales Partner": "Selling", "Sales Partner": "Selling",
"Brand": "Stock" "Brand": "Stock",
"Maintenance Schedule": "Support",
"Maintenance Visit": "Support"
}); });
$.extend(frappe.breadcrumbs.module_map, { $.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; party = frm.doc.party_name;
} }
if (!frm.doc.company) {
frappe.throw(__("Kindly select the company first"));
}
frappe.call({ frappe.call({
method: "erpnext.accounts.party.set_taxes", method: "erpnext.accounts.party.set_taxes",
args: { args: {

View File

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

View File

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

View File

@@ -70,13 +70,23 @@ erpnext.PointOfSale.Payment = class {
this.$invoice_fields.append( this.$invoice_fields.append(
`<div class="invoice_detail_field ${df.fieldname}-field" data-fieldname="${df.fieldname}"></div>` `<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({ this[`${df.fieldname}_field`] = frappe.ui.form.make_control({
df: { df: {
...df, ...df,
onchange: function() { ...df_events
frm.set_value(this.df.fieldname, this.value);
}
}, },
parent: this.$invoice_fields.find(`.${df.fieldname}-field`), parent: this.$invoice_fields.find(`.${df.fieldname}-field`),
render_input: true, render_input: true,

View File

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

View File

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

View File

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

View File

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

View File

@@ -494,8 +494,7 @@ class TestPurchaseReceipt(unittest.TestCase):
"expected_value_after_useful_life": 10, "expected_value_after_useful_life": 10,
"depreciation_method": "Straight Line", "depreciation_method": "Straight Line",
"total_number_of_depreciations": 3, "total_number_of_depreciations": 3,
"frequency_of_depreciation": 1, "frequency_of_depreciation": 1
"depreciation_start_date": frappe.utils.nowdate()
}) })
asset.submit() 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) => { frappe.db.get_value('Stock Settings', {name: 'Stock Settings'}, 'sample_retention_warehouse', (r) => {
if (r.sample_retention_warehouse) { if (r.sample_retention_warehouse) {
var filters = [ var filters = [

View File

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

View File

@@ -11,8 +11,39 @@ def get_level():
activation_level = 0 activation_level = 0
sales_data = [] sales_data = []
min_count = 0 min_count = 0
doctypes = {"Item": 5, "Customer": 5, "Sales Order": 2, "Sales Invoice": 2, "Purchase Order": 2, "Employee": 3, "Lead": 3, "Quotation": 3, doctypes = {
"Payment Entry": 2, "User": 5, "Student": 5, "Instructor": 5, "BOM": 3, "Journal Entry": 3, "Stock Entry": 3} "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): for doctype, min_count in iteritems(doctypes):
count = frappe.db.count(doctype) count = frappe.db.count(doctype)
if count > min_count: if count > min_count:

View File

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