Merge pull request #5631 from KanchanChauhan/address-viewww-fixed
Address view fixed for website
diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js
index b24a17d..ca2d5f0 100644
--- a/erpnext/accounts/doctype/account/account_tree.js
+++ b/erpnext/accounts/doctype/account/account_tree.js
@@ -15,8 +15,8 @@
menu_items:[
{
label: __('New Company'),
- action: function() { newdoc('Company'); },
- condition: 'frappe.boot.user.can_create.indexOf("Company") === -1'
+ action: function() { frappe.new_doc("Company", true) },
+ condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1'
}
],
fields: [
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index 4580d02..5744721 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -12,6 +12,7 @@
["_Test Bank", "Bank Accounts", 0, "Bank", None],
["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"],
["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"],
+ ["_Test Cash", "Cash In Hand", 0, "Cash", None],
["_Test Account Stock Expenses", "Direct Expenses", 1, None, None],
["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
@@ -32,6 +33,7 @@
["_Test Account CST", "Direct Expenses", 0, "Tax", None],
["_Test Account Discount", "Direct Expenses", 0, None, None],
["_Test Write Off", "Indirect Expenses", 0, None, None],
+ ["_Test Exchange Gain/Loss", "Indirect Expenses", 0, None, None],
# related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", 0, None, None],
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index d46c2d5..aca83da 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -91,6 +91,55 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "allow_payment_entry_via_journal_entry",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Allow Payment Entry via Journal Entry",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"description": "Role that is allowed to submit transactions that exceed credit limits set.",
"fieldname": "credit_controller",
"fieldtype": "Link",
@@ -150,7 +199,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-06-27 15:18:27.566087",
+ "modified": "2016-06-27 15:18:28.566087",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
index 3de8241..4b33fd3 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
@@ -3,11 +3,22 @@
frappe.ui.form.on("Bank Reconciliation", {
setup: function(frm) {
- frm.get_docfield("journal_entries").allow_bulk_edit = 1;
+ frm.get_docfield("payment_entries").allow_bulk_edit = 1;
frm.add_fetch("bank_account", "account_currency", "account_currency");
+
+ frm.get_field('payment_entries').grid.editable_fields = [
+ {fieldname: 'against_account', columns: 3},
+ {fieldname: 'amount', columns: 2},
+ {fieldname: 'cheque_number', columns: 3},
+ {fieldname: 'clearance_date', columns: 3}
+ ];
},
onload: function(frm) {
+ var default_bank_account = locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"];
+
+ frm.set_value("bank_account", default_bank_account);
+
frm.set_query("bank_account", function() {
return {
"filters": {
@@ -27,16 +38,21 @@
update_clearance_date: function(frm) {
return frappe.call({
- method: "update_details",
- doc: frm.doc
- });
- },
- get_relevant_entries: function(frm) {
- return frappe.call({
- method: "get_details",
+ method: "update_clearance_date",
doc: frm.doc,
callback: function(r, rt) {
- frm.refresh()
+ frm.refresh_field("payment_entries");
+ frm.refresh_fields();
+ }
+ });
+ },
+ get_payment_entries: function(frm) {
+ return frappe.call({
+ method: "get_payment_entries",
+ doc: frm.doc,
+ callback: function(r, rt) {
+ frm.refresh_field("payment_entries");
+ frm.refresh_fields();
}
});
}
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json
index 59a3cfc..1a7b763 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json
@@ -2,6 +2,7 @@
"allow_copy": 1,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-01-10 16:34:05",
"custom": 0,
"docstatus": 0,
@@ -16,6 +17,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Bank Account",
@@ -40,6 +42,7 @@
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account Currency",
@@ -64,6 +67,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "From Date",
@@ -87,6 +91,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "To Date",
@@ -110,6 +115,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Include Reconciled Entries",
@@ -129,13 +135,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "get_relevant_entries",
+ "fieldname": "get_payment_entries",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Get Relevant Entries",
+ "label": "Get Payment Entries",
"length": 0,
"no_copy": 0,
"options": "",
@@ -153,13 +160,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "journal_entries",
+ "fieldname": "payment_entries",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Journal Entries",
+ "label": "Payment Entries",
"length": 0,
"no_copy": 0,
"options": "Bank Reconciliation Detail",
@@ -181,6 +189,7 @@
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Update Clearance Date",
@@ -205,6 +214,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Total Amount",
@@ -226,6 +236,7 @@
"hide_toolbar": 1,
"icon": "icon-check",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
@@ -233,7 +244,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2015-11-30 12:44:45.105451",
+ "modified": "2016-06-28 13:11:09.396353",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation",
@@ -260,7 +271,10 @@
"write": 1
}
],
+ "quick_entry": 1,
"read_only": 1,
"read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0,
"version": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
index fb95731..b07c82d 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
@@ -12,7 +12,7 @@
}
class BankReconciliation(Document):
- def get_details(self):
+ def get_payment_entries(self):
if not (self.bank_account and self.from_date and self.to_date):
msgprint("Bank Account, From Date and To Date are Mandatory")
return
@@ -22,48 +22,68 @@
condition = "and (clearance_date is null or clearance_date='0000-00-00')"
- dl = frappe.db.sql("""select t1.name, t1.cheque_no, t1.cheque_date, t2.debit_in_account_currency,
- t2.credit_in_account_currency, t1.posting_date, t2.against_account, t1.clearance_date
+ journal_entries = frappe.db.sql("""
+ select
+ "Journal Entry" as payment_document, t1.name as payment_entry,
+ t1.cheque_no as cheque_number, t1.cheque_date,
+ abs(t2.debit_in_account_currency - t2.credit_in_account_currency) as amount,
+ t1.posting_date, t2.against_account, t1.clearance_date
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
where
- t2.parent = t1.name and t2.account = %s
- and t1.posting_date >= %s and t1.posting_date <= %s and t1.docstatus=1
- and ifnull(t1.is_opening, 'No') = 'No' %s
- order by t1.posting_date DESC, t1.name DESC""" %
- ('%s', '%s', '%s', condition), (self.bank_account, self.from_date, self.to_date), as_dict=1)
-
- self.set('journal_entries', [])
+ t2.parent = t1.name and t2.account = %s and t1.docstatus=1
+ and t1.posting_date >= %s and t1.posting_date <= %s
+ and ifnull(t1.is_opening, 'No') = 'No' {0}
+ order by t1.posting_date DESC, t1.name DESC
+ """.format(condition), (self.bank_account, self.from_date, self.to_date), as_dict=1)
+
+ payment_entries = frappe.db.sql("""
+ select
+ "Payment Entry" as payment_document, name as payment_entry,
+ reference_no as cheque_number, reference_date as cheque_date,
+ if(paid_from=%s, paid_amount, received_amount) as amount,
+ posting_date, party as against_account, clearance_date
+ from `tabPayment Entry`
+ where
+ (paid_from=%s or paid_to=%s) and docstatus=1
+ and posting_date >= %s and posting_date <= %s {0}
+ order by
+ posting_date DESC, name DESC
+ """.format(condition),
+ (self.bank_account, self.bank_account, self.bank_account, self.from_date, self.to_date), as_dict=1)
+
+ entries = sorted(list(payment_entries)+list(journal_entries),
+ key=lambda k: k['posting_date'] or getdate(nowdate()))
+
+ self.set('payment_entries', [])
self.total_amount = 0.0
- for d in dl:
- nl = self.append('journal_entries', {})
- nl.posting_date = d.posting_date
- nl.voucher_id = d.name
- nl.cheque_number = d.cheque_no
- nl.cheque_date = d.cheque_date
- nl.debit = d.debit_in_account_currency
- nl.credit = d.credit_in_account_currency
- nl.against_account = d.against_account
- nl.clearance_date = d.clearance_date
- self.total_amount += flt(d.debit_in_account_currency) - flt(d.credit_in_account_currency)
+ for d in entries:
+ row = self.append('payment_entries', {})
+ row.update(d)
+ self.total_amount += flt(d.amount)
- def update_details(self):
- vouchers = []
- for d in self.get('journal_entries'):
+ def update_clearance_date(self):
+ clearance_date_updated = False
+ for d in self.get('payment_entries'):
if d.clearance_date:
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
- frappe.throw(_("Clearance date cannot be before check date in row {0}").format(d.idx))
+ frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
+ .format(d.idx, d.clearance_date, d.cheque_date))
if d.clearance_date or self.include_reconciled_entries:
if not d.clearance_date:
d.clearance_date = None
- frappe.db.set_value("Journal Entry", d.voucher_id, "clearance_date", d.clearance_date)
- frappe.db.sql("""update `tabJournal Entry` set clearance_date = %s, modified = %s
- where name=%s""", (d.clearance_date, nowdate(), d.voucher_id))
- vouchers.append(d.voucher_id)
- if vouchers:
- msgprint("Clearance Date updated in: {0}".format(", ".join(vouchers)))
+ frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
+ frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
+ where name=%s""".format(d.payment_document),
+ (d.clearance_date, nowdate(), d.payment_entry))
+
+ clearance_date_updated = True
+
+ if clearance_date_updated:
+ self.get_payment_entries()
+ msgprint(_("Clearance Date updated"))
else:
msgprint(_("Clearance Date not mentioned"))
diff --git a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json b/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json
index fc3c3b6..b56ad8b 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json
+++ b/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json
@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-02-22 01:27:37",
"custom": 0,
"docstatus": 0,
@@ -11,18 +12,45 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "voucher_id",
+ "fieldname": "payment_document",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment Document",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "payment_entry",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
- "label": "Voucher ID",
+ "label": "Payment Entry",
"length": 0,
"no_copy": 0,
"oldfieldname": "voucher_id",
"oldfieldtype": "Link",
- "options": "Journal Entry",
+ "options": "payment_document",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@@ -42,6 +70,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Against Account",
@@ -64,13 +93,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "debit",
+ "fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 0,
- "label": "Debit",
+ "in_list_view": 1,
+ "label": "Amount",
"length": 0,
"no_copy": 0,
"oldfieldname": "debit",
@@ -90,36 +120,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "credit",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Credit",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "credit",
- "oldfieldtype": "Currency",
- "options": "account_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -144,8 +149,9 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
+ "in_list_view": 0,
"label": "Posting Date",
"length": 0,
"no_copy": 0,
@@ -169,6 +175,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Cheque Number",
@@ -194,6 +201,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Cheque Date",
@@ -219,6 +227,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Clearance Date",
@@ -240,6 +249,7 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
@@ -247,12 +257,15 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-02-17 06:50:40.074578",
+ "modified": "2016-06-28 13:23:15.412477",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation Detail",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cost_center/cost_center_tree.js b/erpnext/accounts/doctype/cost_center/cost_center_tree.js
index ac82f23..d8d45c5 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center_tree.js
+++ b/erpnext/accounts/doctype/cost_center/cost_center_tree.js
@@ -14,8 +14,8 @@
menu_items:[
{
label: __('New Company'),
- action: function() { newdoc('Company'); },
- condition: 'frappe.boot.user.can_create.indexOf("Company") === -1'
+ action: function() { frappe.new_doc("Company", true) },
+ condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1'
}
],
fields:[
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 1f95fb8..b90cd23 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -38,7 +38,7 @@
self.against_voucher)
def check_mandatory(self):
- mandatory = ['account','remarks','voucher_type','voucher_no','company']
+ mandatory = ['account','voucher_type','voucher_no','company']
for k in mandatory:
if not self.get(k):
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index f670c90..cb6c36c 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -283,7 +283,8 @@
type: "GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
- "voucher_type": doc.voucher_type,
+ "account_type": (doc.voucher_type=="Bank Entry" ?
+ "Bank" : (doc.voucher_type=="Cash" ? "Cash" : null)),
"company": doc.company
},
callback: function(r) {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 40386e0..96b95f9 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -525,22 +525,19 @@
d.party_balance = party_balance[(d.party_type, d.party)]
@frappe.whitelist()
-def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None, account=None):
+def get_default_bank_cash_account(company, account_type=None, mode_of_payment=None, account=None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
if mode_of_payment:
- account = get_bank_cash_account(mode_of_payment, company)
- if account.get("account"):
- account.update({"balance": get_balance_on(account.get("account"))})
- return account
+ account = get_bank_cash_account(mode_of_payment, company).get("account")
if not account:
- if voucher_type=="Bank Entry":
+ if account_type=="Bank":
account = frappe.db.get_value("Company", company, "default_bank_account")
if not account:
account = frappe.db.get_value("Account",
{"company": company, "account_type": "Bank", "is_group": 0})
- elif voucher_type=="Cash Entry":
+ elif account_type=="Cash":
account = frappe.db.get_value("Company", company, "default_cash_account")
if not account:
account = frappe.db.get_value("Account",
@@ -549,12 +546,13 @@
if account:
account_details = frappe.db.get_value("Account", account,
["account_currency", "account_type"], as_dict=1)
- return {
+
+ return frappe._dict({
"account": account,
"balance": get_balance_on(account),
"account_currency": account_details.account_currency,
"account_type": account_details.account_type
- }
+ })
@frappe.whitelist()
def get_payment_entry_against_order(dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None):
@@ -662,7 +660,7 @@
bank_row = je.append("accounts")
#make it bank_details
- bank_account = get_default_bank_cash_account(ref_doc.company, "Bank Entry", account=args.get("bank_account"))
+ bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account"))
if bank_account:
bank_row.update(bank_account)
bank_row.exchange_rate = get_exchange_rate(bank_account["account"],
@@ -814,7 +812,7 @@
company_currency = get_company_currency(company)
if account_currency != company_currency:
- if reference_type and reference_name and frappe.get_meta(reference_type).get_field("conversion_rate"):
+ if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
exchange_rate = frappe.db.get_value(reference_type, reference_name, "conversion_rate")
elif account_details and account_details.account_type == "Bank" and \
@@ -831,6 +829,7 @@
# don't return None or 0 as it is multipled with a value and that value could be lost
return exchange_rate or 1
+@frappe.whitelist()
def get_average_exchange_rate(account):
exchange_rate = 0
bank_balance_in_account_currency = get_balance_on(account)
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
index 10807ff..9115c20 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
@@ -2,11 +2,12 @@
// License: GNU General Public License v3. See license.txt
cur_frm.set_query("default_account", "accounts", function(doc, cdt, cdn) {
+ var d = locals[cdt][cdn];
return{
filters: [
['Account', 'account_type', 'in', 'Bank, Cash'],
['Account', 'is_group', '=', 0],
- ['Account', 'company', '=', doc.company]
+ ['Account', 'company', '=', d.company]
]
}
});
diff --git a/erpnext/accounts/doctype/payment_entry/__init__.py b/erpnext/accounts/doctype/payment_entry/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/__init__.py
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
new file mode 100644
index 0000000..230d728
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -0,0 +1,724 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Payment Entry', {
+ onload: function(frm) {
+ if(frm.doc.__islocal) {
+ if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
+ if (!frm.doc.paid_to) frm.set_value("paid_to_account_currency", null);
+ }
+ },
+
+ setup: function(frm) {
+ frm.get_field('references').grid.editable_fields = [
+ {fieldname: 'reference_doctype', columns: 2},
+ {fieldname: 'reference_name', columns: 3},
+ {fieldname: 'outstanding_amount', columns: 3},
+ {fieldname: 'allocated_amount', columns: 3}
+ ];
+
+ var party_account_type = frm.doc.party_type=="Customer" ? "Receivable" : "Payable";
+
+ frm.set_query("paid_from", function() {
+ var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
+ ["Bank", "Cash"] : party_account_type;
+
+ return {
+ filters: {
+ "account_type": ["in", account_types],
+ "is_group": 0,
+ "company": frm.doc.company
+ }
+ }
+ });
+
+ frm.set_query("paid_to", function() {
+ var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
+ ["Bank", "Cash"] : party_account_type;
+
+ return {
+ filters: {
+ "account_type": ["in", account_types],
+ "is_group": 0,
+ "company": frm.doc.company
+ }
+ }
+ });
+
+ frm.set_query("account", "deductions", function() {
+ return {
+ filters: {
+ "is_group": 0,
+ "company": frm.doc.company
+ }
+ }
+ });
+
+ frm.set_query("cost_center", "deductions", function() {
+ return {
+ filters: {
+ "is_group": 0,
+ "company": frm.doc.company
+ }
+ }
+ });
+
+ frm.set_query("reference_doctype", "references", function() {
+ if (frm.doc.party_type=="Customer") {
+ var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry"];
+ } else if (frm.doc.party_type=="Supplier") {
+ var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
+ } else {
+ var doctypes = ["Journal Entry"];
+ }
+
+ return {
+ filters: { "name": ["in", doctypes] }
+ };
+ });
+ },
+
+ refresh: function(frm) {
+ frm.events.hide_unhide_fields(frm);
+ frm.events.set_dynamic_labels(frm);
+ frm.events.show_general_ledger(frm);
+ },
+
+ hide_unhide_fields: function(frm) {
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ frm.toggle_display("source_exchange_rate",
+ (frm.doc.paid_amount && frm.doc.paid_from_account_currency != company_currency));
+
+ frm.toggle_display("target_exchange_rate", (frm.doc.received_amount &&
+ frm.doc.paid_to_account_currency != company_currency &&
+ frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
+
+ frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
+
+ frm.toggle_display("base_received_amount", (frm.doc.paid_to_account_currency != company_currency &&
+ frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
+
+ frm.toggle_display("received_amount",
+ frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)
+
+ frm.toggle_display(["base_total_allocated_amount"],
+ (frm.doc.paid_amount && frm.doc.received_amount && frm.doc.base_total_allocated_amount &&
+ ((frm.doc.payment_type=="Receive" && frm.doc.paid_from_account_currency != company_currency) ||
+ (frm.doc.payment_type=="Pay" && frm.doc.paid_to_account_currency != company_currency))));
+
+ var party_amount = frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_amount : frm.doc.received_amount;
+
+ frm.toggle_display("write_off_difference_amount", (frm.doc.difference_amount && frm.doc.party &&
+ (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) &&
+ (frm.doc.total_allocated_amount > party_amount)));
+
+ frm.toggle_display("set_exchange_gain_loss",
+ (frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount &&
+ (frm.doc.paid_from_account_currency != company_currency ||
+ frm.doc.paid_to_account_currency != company_currency)));
+
+ frm.refresh_fields();
+ },
+
+ set_dynamic_labels: function(frm) {
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ var field_label_map = {};
+ var grid_field_label_map = {};
+
+ var setup_field_label_map = function(fields_list, currency, parentfield) {
+ var doctype = parentfield ? frm.fields_dict[parentfield].grid.doctype : frm.doc.doctype;
+ $.each(fields_list, function(i, fname) {
+ var docfield = frappe.meta.docfield_map[doctype][fname];
+ if(docfield) {
+ var label = __(docfield.label || "").replace(/\([^\)]*\)/g, "");
+ if(parentfield) {
+ grid_field_label_map[doctype + "-" + fname] =
+ label.trim() + " (" + __(currency) + ")";
+ } else {
+ field_label_map[fname] = label.trim() + " (" + currency + ")";
+ }
+ }
+ });
+ }
+
+ setup_field_label_map(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
+ "difference_amount"], company_currency);
+
+ setup_field_label_map(["paid_amount"], frm.doc.paid_from_account_currency);
+ setup_field_label_map(["received_amount"], frm.doc.paid_to_account_currency);
+
+ var party_account_currency = frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency;
+
+ setup_field_label_map(["total_allocated_amount", "unallocated_amount"], party_account_currency);
+
+ $.each(field_label_map, function(fname, label) {
+ me.frm.fields_dict[fname].set_label(label);
+ });
+
+ setup_field_label_map(["total_amount", "outstanding_amount", "allocated_amount"],
+ party_account_currency, "references");
+
+ setup_field_label_map(["amount"], company_currency, "deductions");
+
+ $.each(grid_field_label_map, function(fname, label) {
+ fname = fname.split("-");
+ var df = frappe.meta.get_docfield(fname[0], fname[1], me.frm.doc.name);
+ if(df) df.label = label;
+ });
+
+ cur_frm.set_df_property("source_exchange_rate", "description",
+ ("1 " + frm.doc.paid_from_account_currency + " = [?] " + company_currency));
+
+ cur_frm.set_df_property("target_exchange_rate", "description",
+ ("1 " + frm.doc.paid_to_account_currency + " = [?] " + company_currency));
+
+ frm.refresh_fields();
+ },
+
+ show_general_ledger: function(frm) {
+ if(frm.doc.docstatus==1) {
+ frm.add_custom_button(__('Ledger'), function() {
+ frappe.route_options = {
+ "voucher_no": frm.doc.name,
+ "from_date": frm.doc.posting_date,
+ "to_date": frm.doc.posting_date,
+ "company": frm.doc.company,
+ group_by_voucher: 0
+ };
+ frappe.set_route("query-report", "General Ledger");
+ }, "icon-table");
+ }
+ },
+
+ payment_type: function(frm) {
+ if(frm.doc.payment_type == "Internal Transfer") {
+ $.each(["party", "party_balance", "paid_from", "paid_to",
+ "references", "total_allocated_amount"], function(i, field) {
+ frm.set_value(field, null);
+ })
+ } else {
+ if(!frm.doc.party)
+ frm.set_value("party_type", frm.doc.payment_type=="Receive" ? "Customer" : "Supplier");
+ else
+ frm.events.party(frm);
+
+ if(frm.doc.mode_of_payment)
+ frm.events.mode_of_payment(frm);
+ }
+ },
+
+ party_type: function(frm) {
+ if(frm.doc.party) {
+ $.each(["party", "party_balance", "paid_from", "paid_to",
+ "paid_from_account_currency", "paid_from_account_balance",
+ "paid_to_account_currency", "paid_to_account_balance",
+ "references", "total_allocated_amount"], function(i, field) {
+ frm.set_value(field, null);
+ })
+ }
+ },
+
+ party: function(frm) {
+ if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) {
+ frm.set_party_account_based_on_party = true;
+
+ return frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details",
+ args: {
+ company: frm.doc.company,
+ party_type: frm.doc.party_type,
+ party: frm.doc.party,
+ date: frm.doc.posting_date
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ if(frm.doc.payment_type == "Receive") {
+ frm.set_value("paid_from", r.message.party_account);
+ frm.set_value("paid_from_account_currency", r.message.party_account_currency);
+ frm.set_value("paid_from_account_balance", r.message.account_balance);
+ } else if (frm.doc.payment_type == "Pay"){
+ frm.set_value("paid_to", r.message.party_account);
+ frm.set_value("paid_to_account_currency", r.message.party_account_currency);
+ frm.set_value("paid_to_account_balance", r.message.account_balance);
+ }
+ frm.set_value("party_balance", r.message.party_balance);
+ frm.events.get_outstanding_documents(frm);
+ frm.events.hide_unhide_fields(frm);
+ frm.events.set_dynamic_labels(frm);
+ frm.set_party_account_based_on_party = false;
+ }
+ }
+ });
+ }
+ },
+
+ mode_of_payment: function(frm) {
+ return frappe.call({
+ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
+ args: {
+ "mode_of_payment": frm.doc.mode_of_payment,
+ "company": frm.doc.company
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ var payment_account_field = frm.doc.payment_type == "Receive" ? "paid_to" : "paid_from";
+ frm.set_value(payment_account_field, r.message['account']);
+ }
+ }
+ });
+ },
+
+ paid_from: function(frm) {
+ if(frm.set_party_account_based_on_party) return;
+
+ frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from,
+ "paid_from_account_currency", "paid_from_account_balance", function(frm) {
+ if(frm.doc.payment_type == "Receive") frm.events.get_outstanding_documents(frm);
+ }
+ );
+ },
+
+ paid_to: function(frm) {
+ if(frm.set_party_account_based_on_party) return;
+
+ frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to,
+ "paid_to_account_currency", "paid_to_account_balance", function(frm) {
+ if(frm.doc.payment_type == "Pay") frm.events.get_outstanding_documents(frm);
+ }
+ );
+ },
+
+ set_account_currency_and_balance: function(frm, account, currency_field,
+ balance_field, callback_function) {
+ frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details",
+ args: {
+ "account": account,
+ "date": frm.doc.posting_date
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ frm.set_value(currency_field, r.message['account_currency']);
+ frm.set_value(balance_field, r.message['account_balance']);
+
+ if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") {
+ frm.toggle_reqd(["reference_no", "reference_date"],
+ (r.message['account_type'] == "Bank" ? 1 : 0));
+ if(!frm.doc.received_amount && frm.doc.paid_amount)
+ frm.events.paid_amount(frm);
+ } else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") {
+ frm.toggle_reqd(["reference_no", "reference_date"],
+ (r.message['account_type'] == "Bank" ? 1 : 0));
+
+ if(!frm.doc.paid_amount && frm.doc.received_amount)
+ frm.events.received_amount(frm);
+ }
+
+ if(callback_function) callback_function(frm);
+
+ frm.events.hide_unhide_fields(frm);
+ frm.events.set_dynamic_labels(frm);
+ }
+ }
+ });
+ },
+
+ paid_from_account_currency: function(frm) {
+ if(!frm.doc.paid_from_account_currency) return;
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ if (frm.doc.paid_from_account_currency == company_currency) {
+ frm.set_value("source_exchange_rate", 1);
+ } else if (frm.doc.paid_from){
+ if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) {
+ frappe.call({
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_average_exchange_rate",
+ args: {
+ account: frm.doc.paid_from
+ },
+ callback: function(r, rt) {
+ frm.set_value("source_exchange_rate", r.message);
+ }
+ })
+ } else {
+ frm.events.set_current_exchange_rate(frm, "source_exchange_rate",
+ frm.doc.paid_from_account_currency, company_currency);
+ }
+ }
+ },
+
+ paid_to_account_currency: function(frm) {
+ if(!frm.doc.paid_to_account_currency) return;
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ frm.events.set_current_exchange_rate(frm, "target_exchange_rate",
+ frm.doc.paid_to_account_currency, company_currency);
+ },
+
+ set_current_exchange_rate: function(frm, exchange_rate_field, from_currency, to_currency) {
+ frappe.call({
+ method: "erpnext.setup.utils.get_exchange_rate",
+ args: {
+ from_currency: from_currency,
+ to_currency: to_currency
+ },
+ callback: function(r, rt) {
+ frm.set_value(exchange_rate_field, r.message);
+ }
+ })
+ },
+
+ source_exchange_rate: function(frm) {
+ if (frm.doc.paid_amount) {
+ frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
+ }
+ },
+
+ target_exchange_rate: function(frm) {
+ if (frm.doc.received_amount) {
+ frm.set_value("base_received_amount",
+ flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate));
+ }
+ },
+
+ paid_amount: function(frm) {
+ frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
+
+ if(!frm.set_paid_amount_based_on_received_amount &&
+ (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) {
+ frm.set_value("received_amount", frm.doc.paid_amount);
+ frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
+ frm.set_value("base_received_amount", frm.doc.base_paid_amount);
+ }
+ if(frm.doc.payment_type == "Receive")
+ frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount);
+ else
+ frm.events.set_difference_amount(frm);
+
+ frm.set_paid_amount_based_on_received_amount = false;
+ },
+
+ received_amount: function(frm) {
+ frm.set_paid_amount_based_on_received_amount = true;
+
+ if(!frm.doc.paid_amount && frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
+ frm.set_value("paid_amount", frm.doc.received_amount);
+ frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate);
+ frm.set_value("base_paid_amount", frm.doc.base_received_amount);
+ }
+
+ frm.set_value("base_received_amount",
+ flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate));
+
+ if(frm.doc.payment_type == "Pay")
+ frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
+ else
+ frm.events.set_difference_amount(frm);
+ },
+
+ get_outstanding_documents: function(frm) {
+ frm.clear_table("references");
+
+ if(!frm.doc.party) return;
+
+ frm.events.check_mandatory_to_fetch(frm);
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ return frappe.call({
+ method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents',
+ args: {
+ args: {
+ "company": frm.doc.company,
+ "party_type": frm.doc.party_type,
+ "payment_type": frm.doc.payment_type,
+ "party": frm.doc.party,
+ "party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to
+ }
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ var total_positive_outstanding = 0;
+ var total_negative_outstanding = 0;
+
+ $.each(r.message, function(i, d) {
+ var c = frm.add_child("references");
+ c.reference_doctype = d.voucher_type;
+ c.reference_name = d.voucher_no;
+ c.total_amount = d.invoice_amount;
+ c.outstanding_amount = d.outstanding_amount;
+ if(!in_list(["Sales Order", "Purchase Order"], d.voucher_type)) {
+ if(flt(d.outstanding_amount) > 0)
+ total_positive_outstanding += flt(d.outstanding_amount);
+ else
+ total_negative_outstanding += Math.abs(flt(d.outstanding_amount));
+ }
+
+ var party_account_currency = frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency;
+
+ if(party_account_currency != company_currency) {
+ c.exchange_rate = d.exchange_rate;
+ } else {
+ c.exchange_rate = 1;
+ }
+ if (in_list(['Sales Invoice', 'Purchase Invoice'], d.reference_doctype)){
+ c.due_date = d.due_date;
+ }
+ });
+
+ if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
+ (frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")) {
+ if(total_positive_outstanding > total_negative_outstanding)
+ frm.set_value("paid_amount",
+ total_positive_outstanding - total_negative_outstanding);
+ } else if (total_negative_outstanding &&
+ (total_positive_outstanding < total_negative_outstanding)) {
+ frm.set_value("received_amount",
+ total_negative_outstanding - total_positive_outstanding);
+ }
+ }
+
+ frm.events.allocate_party_amount_against_ref_docs(frm,
+ (frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount));
+ }
+ });
+ },
+
+ allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
+ var total_positive_outstanding_including_order = 0;
+ var total_negative_outstanding = 0;
+
+ $.each(frm.doc.references || [], function(i, row) {
+ if(flt(row.outstanding_amount) > 0)
+ total_positive_outstanding_including_order += flt(row.outstanding_amount);
+ else
+ total_negative_outstanding += Math.abs(flt(row.outstanding_amount));
+ })
+
+ var allocated_negative_outstanding = 0;
+ if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
+ (frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")) {
+ if(total_positive_outstanding_including_order > paid_amount) {
+ var remaining_outstanding = total_positive_outstanding_including_order - paid_amount;
+ allocated_negative_outstanding = total_negative_outstanding < remaining_outstanding ?
+ total_negative_outstanding : remaining_outstanding;
+ }
+
+ var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding;
+ } else {
+ if(paid_amount > total_negative_outstanding) {
+ if(total_negative_outstanding == 0) {
+ frappe.msgprint(__("Cannot {0} {1} {2} without any negative outstanding invoice",
+ [frm.doc.payment_type,
+ (frm.doc.party_type=="Customer" ? "to" : "from"), frm.doc.party_type]));
+ return false
+ } else {
+ frappe.msgprint(__("Paid Amount cannot be greater than total negative outstanding amount {0}", [total_negative_outstanding]));
+ return false;
+ }
+ } else {
+ allocated_positive_outstanding = total_negative_outstanding - paid_amount;
+ allocated_negative_outstanding = paid_amount +
+ (total_positive_outstanding_including_order < allocated_positive_outstanding ?
+ total_positive_outstanding_including_order : allocated_positive_outstanding)
+ }
+ }
+
+ $.each(frm.doc.references || [], function(i, row) {
+ row.allocated_amount = 0
+
+ if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
+ if(row.outstanding_amount >= allocated_positive_outstanding)
+ row.allocated_amount = allocated_positive_outstanding;
+ else row.allocated_amount = row.outstanding_amount;
+
+ allocated_positive_outstanding -= flt(row.allocated_amount);
+ } else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
+ if(Math.abs(row.outstanding_amount) >= allocated_negative_outstanding)
+ row.allocated_amount = -1*allocated_negative_outstanding;
+ else row.allocated_amount = row.outstanding_amount;
+
+ allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount));
+ }
+ })
+ frm.refresh_fields()
+ frm.events.set_total_allocated_amount(frm);
+ },
+
+ set_total_allocated_amount: function(frm) {
+ var total_allocated_amount = base_total_allocated_amount = 0.0;
+ $.each(frm.doc.references || [], function(i, row) {
+ if (row.allocated_amount) {
+ total_allocated_amount += flt(row.allocated_amount);
+ base_total_allocated_amount += flt(flt(row.allocated_amount)*flt(row.exchange_rate),
+ precision("base_paid_amount"));
+ }
+ });
+ frm.set_value("total_allocated_amount", Math.abs(total_allocated_amount));
+ frm.set_value("base_total_allocated_amount", Math.abs(base_total_allocated_amount));
+
+ frm.events.set_difference_amount(frm);
+ },
+
+ set_difference_amount: function(frm) {
+ var unallocated_amount = 0;
+ if(frm.doc.party) {
+ var party_amount = frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_amount : frm.doc.received_amount;
+
+ if(frm.doc.total_allocated_amount < party_amount)
+ unallocated_amount = party_amount - frm.doc.total_allocated_amount;
+ }
+
+ frm.set_value("unallocated_amount", unallocated_amount);
+
+ var difference_amount = 0;
+ var base_unallocated_amount = flt(frm.doc.unallocated_amount) *
+ (frm.doc.payment_type=="Receive" ? frm.doc.source_exchange_rate : frm.doc.target_exchange_rate);
+
+ var base_party_amount = flt(frm.doc.base_total_allocated_amount) + base_unallocated_amount;
+
+ if(frm.doc.payment_type == "Receive") {
+ difference_amount = base_party_amount - flt(frm.doc.base_received_amount);
+ } else if (frm.doc.payment_type == "Pay") {
+ difference_amount = flt(frm.doc.base_paid_amount) - base_party_amount;
+ } else {
+ difference_amount = flt(frm.doc.base_paid_amount) - flt(frm.doc.base_received_amount);
+ }
+
+ $.each(frm.doc.deductions || [], function(i, d) {
+ if(d.amount) difference_amount -= flt(d.amount);
+ })
+
+ frm.set_value("difference_amount", difference_amount);
+
+ frm.events.hide_unhide_fields(frm);
+ },
+
+ check_mandatory_to_fetch: function(frm) {
+ $.each(["Company", "Party Type", "Party", "payment_type"], function(i, field) {
+ if(!frm.doc[frappe.model.scrub(field)]) {
+ frappe.msgprint(__("Please select {0} first", [field]));
+ return false;
+ }
+
+ });
+ },
+
+ validate_reference_document: function(frm, row) {
+ var _validate = function(i, row) {
+ if (!row.reference_doctype) {
+ return;
+ }
+
+ if(frm.doc.party_type=="Customer"
+ && !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.reference_doctype)) {
+ frappe.model.set_value(row.doctype, row.name, "reference_doctype", null);
+ frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice or Journal Entry", [row.idx]));
+ return false;
+ }
+
+ if(frm.doc.party_type=="Supplier" && !in_list(["Purchase Order",
+ "Purchase Invoice", "Journal Entry"], row.reference_doctype)) {
+ frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null);
+ frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
+ return false;
+ }
+ }
+
+ if (row) {
+ _validate(0, row);
+ } else {
+ $.each(frm.doc.vouchers || [], _validate);
+ }
+ },
+
+ write_off_difference_amount: function(frm) {
+ frm.events.set_deductions_entry(frm, "write_off_account");
+ },
+
+ set_exchange_gain_loss: function(frm) {
+ frm.events.set_deductions_entry(frm, "exchange_gain_loss_account");
+ },
+
+ set_deductions_entry: function(frm, account) {
+ if(frm.doc.difference_amount) {
+ frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_company_defaults",
+ args: {
+ company: frm.doc.company
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ var write_off_row = $.map(frm.doc["deductions"] || [], function(t) {
+ return t.account==r.message[account] ? t : null; });
+
+ if (!write_off_row.length) {
+ var row = frm.add_child("deductions");
+ row.account = r.message[account];
+ row.cost_center = r.message["cost_center"];
+ } else {
+ var row = write_off_row[0];
+ }
+
+ row.amount = flt(row.amount) + flt(frm.doc.difference_amount);
+ refresh_field("deductions");
+
+ frm.events.set_difference_amount(frm);
+ }
+ }
+ })
+ }
+ }
+});
+
+
+frappe.ui.form.on('Payment Entry Reference', {
+ reference_doctype: function(frm, cdt, cdn) {
+ var row = locals[cdt][cdn];
+ frm.events.validate_reference_document(frm, row);
+ },
+
+ reference_name: function(frm, cdt, cdn) {
+ var row = locals[cdt][cdn];
+ return frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_reference_details",
+ args: {
+ reference_doctype: row.reference_doctype,
+ reference_name: row.reference_name,
+ party_account_currency: frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ $.each(r.message, function(field, value) {
+ frappe.model.set_value(cdt, cdn, field, value);
+ })
+ frm.refresh_fields();
+ }
+ }
+ })
+ },
+
+ allocated_amount: function(frm) {
+ frm.events.set_total_allocated_amount(frm);
+ },
+
+ references_remove: function(frm) {
+ frm.events.set_total_allocated_amount(frm);
+ }
+})
+
+frappe.ui.form.on('Payment Entry Deduction', {
+ amount: function(frm) {
+ frm.events.set_difference_amount(frm);
+ },
+
+ deductions_remove: function(frm) {
+ frm.events.set_difference_amount(frm);
+ }
+})
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
new file mode 100644
index 0000000..d40ba9d
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -0,0 +1,1430 @@
+{
+ "allow_copy": 0,
+ "allow_import": 1,
+ "allow_rename": 0,
+ "autoname": "naming_series:",
+ "beta": 0,
+ "creation": "2016-06-01 14:38:51.012597",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Series",
+ "length": 0,
+ "no_copy": 0,
+ "options": "PE-",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "fieldname": "payment_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Receive\nPay\nInternal Transfer",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Posting Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Company",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "mode_of_payment",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Mode of Payment",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Mode of Payment",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)",
+ "fieldname": "party_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)",
+ "fieldname": "party_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Party Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Customer\nSupplier",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_10",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.party_type",
+ "fieldname": "party",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Party",
+ "length": 0,
+ "no_copy": 0,
+ "options": "party_type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "party",
+ "fieldname": "party_balance",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Party Balance",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "payment_accounts_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(in_list([\"Internal Transfer\", \"Pay\"], doc.payment_type) || doc.party)",
+ "fieldname": "paid_from",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Paid From",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "paid_from",
+ "fieldname": "paid_from_account_currency",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Currency",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "paid_from",
+ "fieldname": "paid_from_account_balance",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Balance",
+ "length": 0,
+ "no_copy": 0,
+ "options": "paid_from_account_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:(in_list([\"Internal Transfer\", \"Receive\"], doc.payment_type) || doc.party)",
+ "fieldname": "paid_to",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Paid To",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "paid_to",
+ "fieldname": "paid_to_account_currency",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Currency",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "paid_to",
+ "fieldname": "paid_to_account_balance",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Balance",
+ "length": 0,
+ "no_copy": 0,
+ "options": "paid_to_account_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": "",
+ "depends_on": "eval:(doc.paid_to && doc.paid_from)",
+ "fieldname": "payment_amounts_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "paid_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Paid Amount",
+ "length": 0,
+ "no_copy": 0,
+ "options": "paid_from_account_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "source_exchange_rate",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Exchange Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "base_paid_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Paid Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_21",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "received_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Received Amount",
+ "length": 0,
+ "no_copy": 0,
+ "options": "paid_to_account_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "target_exchange_rate",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Exchange Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "base_received_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Received Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "references",
+ "depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
+ "fieldname": "section_break_14",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Reference Documents",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "references",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment References",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Entry Reference",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "section_break_34",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_amount && doc.received_amount && doc.references)",
+ "fieldname": "total_allocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Allocated Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "base_total_allocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Allocated Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "difference_amount",
+ "fieldname": "write_off_difference_amount",
+ "fieldtype": "Button",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Write Off Difference Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "set_exchange_gain_loss",
+ "fieldtype": "Button",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Set Exchange Gain / Loss",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_36",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_amount && doc.received_amount && doc.references)",
+ "fieldname": "unallocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Unallocated Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_amount && doc.received_amount)",
+ "fieldname": "difference_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Difference Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "deductions",
+ "depends_on": "eval:(doc.paid_amount && doc.received_amount)",
+ "fieldname": "deductions_or_loss_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Deductions or Loss",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "deductions",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment Deductions or Loss",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Entry Deduction",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "transaction_references",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Transaction References",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_from && doc.paid_to)",
+ "fieldname": "reference_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Cheque/Reference No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_23",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_from && doc.paid_to)",
+ "fieldname": "reference_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Cheque/Reference Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:doc.docstatus==1",
+ "fieldname": "clearance_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Clearance Date",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "",
+ "depends_on": "eval:(doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
+ "fieldname": "section_break_12",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "More Information",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Project",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Project",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Remarks",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_16",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "letter_head",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Letter Head",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Letter Head",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "print_heading",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Print Heading",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Print Heading",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Amended From",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Payment Entry",
+ "permlevel": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Title",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 1,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2016-07-01 17:14:17.562924",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Entry",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 1,
+ "apply_user_permissions": 0,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "apply_user_permissions": 0,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
new file mode 100644
index 0000000..d4d93a7
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -0,0 +1,666 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, json
+from frappe import _, scrub, ValidationError
+from frappe.utils import flt, comma_or, nowdate
+from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
+from erpnext.accounts.party import get_party_account
+from erpnext.accounts.doctype.journal_entry.journal_entry \
+ import get_average_exchange_rate, get_default_bank_cash_account
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.accounts.general_ledger import make_gl_entries
+
+from erpnext.controllers.accounts_controller import AccountsController
+
+class InvalidPaymentEntry(ValidationError): pass
+
+class PaymentEntry(AccountsController):
+ def setup_party_account_field(self):
+ self.party_account_field = None
+ self.party_account = None
+ self.party_account_currency = None
+
+ if self.payment_type == "Receive":
+ self.party_account_field = "paid_from"
+ self.party_account = self.paid_from
+ self.party_account_currency = self.paid_from_account_currency
+
+ elif self.payment_type == "Pay":
+ self.party_account_field = "paid_to"
+ self.party_account = self.paid_to
+ self.party_account_currency = self.paid_to_account_currency
+
+ def validate(self):
+ self.setup_party_account_field()
+ self.set_missing_values()
+ self.validate_party_details()
+ self.validate_bank_accounts()
+ self.set_exchange_rate()
+ self.validate_mandatory()
+ self.validate_reference_documents()
+ self.set_amounts()
+ self.clear_unallocated_reference_document_rows()
+ self.validate_payment_against_negative_invoice()
+ self.validate_transaction_reference()
+ self.set_title()
+ self.set_remarks()
+
+ def on_submit(self):
+ if self.difference_amount:
+ frappe.throw(_("Difference Amount must be zero"))
+ self.make_gl_entries()
+ self.update_advance_paid()
+
+ def on_cancel(self):
+ self.setup_party_account_field()
+ self.make_gl_entries(cancel=1)
+ self.update_advance_paid()
+
+ def set_missing_values(self):
+ if self.payment_type == "Internal Transfer":
+ for field in ("party", "party_balance", "total_allocated_amount",
+ "base_total_allocated_amount", "unallocated_amount"):
+ self.set(field, None)
+ self.references = []
+ else:
+ if not self.party_type:
+ frappe.throw(_("Party Type is mandatory"))
+
+ if not self.party:
+ frappe.throw(_("Party is mandatory"))
+
+ if self.party:
+ if not self.party_balance:
+ self.party_balance = get_balance_on(party_type=self.party_type,
+ party=self.party, date=self.posting_date)
+
+ if not self.party_account:
+ party_account = get_party_account(self.party_type, self.party, self.company)
+ self.set(self.party_account_field, party_account)
+ self.party_account = party_account
+
+ if self.paid_from and not (self.paid_from_account_currency or self.paid_from_account_balance):
+ acc = get_account_details(self.paid_from, self.posting_date)
+ self.paid_from_account_currency = acc.account_currency
+ self.paid_from_account_balance = acc.account_balance
+
+ if self.paid_to and not (self.paid_to_account_currency or self.paid_to_account_balance):
+ acc = get_account_details(self.paid_to, self.posting_date)
+ self.paid_to_account_currency = acc.account_currency
+ self.paid_to_account_balance = acc.account_balance
+
+ self.party_account_currency = self.paid_from_account_currency \
+ if self.payment_type=="Receive" else self.paid_to_account_currency
+
+ self.set_missing_ref_details()
+
+
+ def set_missing_ref_details(self):
+ for d in self.get("references"):
+ if d.allocated_amount:
+ ref_details = get_reference_details(d.reference_doctype,
+ d.reference_name, self.party_account_currency)
+
+ for field, value in ref_details.items():
+ if not d.get(field):
+ d.set(field, value)
+
+ def validate_party_details(self):
+ if self.party:
+ if not frappe.db.exists(self.party_type, self.party):
+ frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
+
+ if self.party_account:
+ party_account_type = "Receivable" if self.party_type=="Customer" else "Payable"
+ self.validate_account_type(self.party_account, [party_account_type])
+
+ def validate_bank_accounts(self):
+ if self.payment_type in ("Pay", "Internal Transfer"):
+ self.validate_account_type(self.paid_from, ["Bank", "Cash"])
+
+ if self.payment_type in ("Receive", "Internal Transfer"):
+ self.validate_account_type(self.paid_to, ["Bank", "Cash"])
+
+ def validate_account_type(self, account, account_types):
+ account_type = frappe.db.get_value("Account", account, "account_type")
+ if account_type not in account_types:
+ frappe.throw(_("Account Type for {0} must be {1}").format(comma_or(account_types)))
+
+ def set_exchange_rate(self):
+ if self.paid_from and not self.source_exchange_rate:
+ if self.paid_from_account_currency == self.company_currency:
+ self.source_exchange_rate = 1
+ elif self.payment_type in ("Pay", "Internal Transfer"):
+ self.source_exchange_rate = get_average_exchange_rate(self.paid_from)
+ else:
+ self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency,
+ self.company_currency)
+
+ if self.paid_to and not self.target_exchange_rate:
+ self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency,
+ self.company_currency)
+
+ def validate_mandatory(self):
+ for field in ("paid_amount", "received_amount", "source_exchange_rate", "target_exchange_rate"):
+ if not self.get(field):
+ frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
+
+ def validate_reference_documents(self):
+ if self.party_type == "Customer":
+ valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry")
+ else:
+ valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
+
+ for d in self.get("references"):
+ if not d.allocated_amount:
+ continue
+ if d.reference_doctype not in valid_reference_doctypes:
+ frappe.throw(_("Reference Doctype must be one of {0}")
+ .format(comma_or(valid_reference_doctypes)))
+
+ elif d.reference_name:
+ if not frappe.db.exists(d.reference_doctype, d.reference_name):
+ frappe.throw(_("{0} {1} does not exist").format(d.reference_doctype, d.reference_name))
+ else:
+ ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name)
+
+ if d.reference_doctype != "Journal Entry":
+ if self.party != ref_doc.get(scrub(self.party_type)):
+ frappe.throw(_("{0} {1} does not associated with {2} {3}")
+ .format(d.reference_doctype, d.reference_name, self.party_type, self.party))
+ else:
+ self.validate_journal_entry()
+
+ if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
+ ref_party_account = ref_doc.debit_to \
+ if self.party_type=="Customer" else ref_doc.credit_to
+ if ref_party_account != self.party_account:
+ frappe.throw(_("{0} {1} does not associated with Party Account {2}")
+ .format(d.reference_doctype, d.reference_name, self.party_account))
+
+ if ref_doc.docstatus != 1:
+ frappe.throw(_("{0} {1} must be submitted")
+ .format(d.reference_doctype, d.reference_name))
+
+ def validate_journal_entry(self):
+ for d in self.get("references"):
+ if d.allocated_amount and d.reference_doctype == "Journal Entry":
+ je_accounts = frappe.db.sql("""select debit, credit from `tabJournal Entry Account`
+ where account = %s and party=%s and docstatus = 1 and parent = %s
+ and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order"))
+ """, (self.party_account, self.party, d.reference_name), as_dict=True)
+
+ if not je_accounts:
+ frappe.throw(_("Row #{0}: Journal Entry {0} does not have account {1} or already matched against another voucher")
+ .format(d.idx, d.reference_name, self.party_account))
+ else:
+ dr_or_cr = "debit" if self.payment_type == "Receive" else "credit"
+ valid = False
+ for jvd in je_accounts:
+ if flt(jvd[dr_or_cr]) > 0:
+ valid = True
+ if not valid:
+ frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
+ .format(d.reference_name, dr_or_cr))
+
+ def set_amounts(self):
+ self.set_amounts_in_company_currency()
+ self.set_total_allocated_amount()
+ self.set_unallocated_amount()
+ self.set_difference_amount()
+
+ def set_amounts_in_company_currency(self):
+ self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0
+ if self.paid_amount:
+ self.base_paid_amount = flt(flt(self.paid_amount) * flt(self.source_exchange_rate),
+ self.precision("base_paid_amount"))
+
+ if self.received_amount:
+ self.base_received_amount = flt(flt(self.received_amount) * flt(self.target_exchange_rate),
+ self.precision("base_received_amount"))
+
+ def set_total_allocated_amount(self):
+ if self.payment_type == "Internal Transfer":
+ return
+
+ total_allocated_amount, base_total_allocated_amount = 0, 0
+ for d in self.get("references"):
+ if d.allocated_amount:
+ total_allocated_amount += flt(d.allocated_amount)
+ base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate),
+ self.precision("base_paid_amount"))
+
+ self.total_allocated_amount = abs(total_allocated_amount)
+ self.base_total_allocated_amount = abs(base_total_allocated_amount)
+
+ def set_unallocated_amount(self):
+ self.unallocated_amount = 0;
+ if self.party:
+ party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
+
+ if self.total_allocated_amount < party_amount:
+ self.unallocated_amount = party_amount - self.total_allocated_amount
+
+ def set_difference_amount(self):
+ base_unallocated_amount = self.unallocated_amount * \
+ (self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
+
+ base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
+
+ if self.payment_type == "Receive":
+ self.difference_amount = base_party_amount - self.base_received_amount
+ elif self.payment_type == "Pay":
+ self.difference_amount = self.base_paid_amount - base_party_amount
+ else:
+ self.difference_amount = self.base_paid_amount - flt(self.base_received_amount)
+
+ for d in self.get("deductions"):
+ if d.amount:
+ self.difference_amount -= flt(d.amount)
+
+ def clear_unallocated_reference_document_rows(self):
+ self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]}))
+
+ frappe.db.sql("""delete from `tabPayment Entry Reference`
+ where parent = %s and allocated_amount = 0""", self.name)
+
+ def validate_payment_against_negative_invoice(self):
+ if ((self.payment_type=="Pay" and self.party_type=="Customer")
+ or (self.payment_type=="Receive" and self.party_type=="Supplier")):
+ total_negative_outstanding = sum([abs(flt(d.outstanding_amount))
+ for d in self.get("references") if flt(d.outstanding_amount) < 0])
+
+ party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
+
+ if not total_negative_outstanding:
+ frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice")
+ .format(self.payment_type, ("to" if self.party_type=="Customer" else "from"),
+ self.party_type), InvalidPaymentEntry)
+
+ elif party_amount > total_negative_outstanding:
+ frappe.throw(_("Paid Amount cannot be greater than total negative outstanding amount {0}")
+ .format(total_negative_outstanding), InvalidPaymentEntry)
+
+ def set_title(self):
+ if self.payment_type in ("Receive", "Pay"):
+ self.title = self.party
+ else:
+ self.title = self.paid_from + " - " + self.paid_to
+
+ def validate_transaction_reference(self):
+ bank_account = self.paid_to if self.payment_type == "Receive" else self.paid_from
+ bank_account_type = frappe.db.get_value("Account", bank_account, "account_type")
+
+ if bank_account_type == "Bank":
+ if not self.reference_no or not self.reference_date:
+ frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
+
+ def set_remarks(self):
+ if self.remarks: return
+
+ if self.payment_type=="Internal Transfer":
+ remarks = [_("Amount {0} {1} transferred from {2} to {3}")
+ .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
+ else:
+
+ remarks = [_("Amount {0} {1} {2} {3}").format(
+ self.party_account_currency,
+ self.paid_amount if self.payment_type=="Receive" else self.received_amount,
+ _("received from") if self.payment_type=="Receive" else _("to"), self.party
+ )]
+
+ if self.reference_no:
+ remarks.append(_("Transaction reference no {0} dated {1}")
+ .format(self.reference_no, self.reference_date))
+
+ if self.payment_type in ["Receive", "Pay"]:
+ for d in self.get("references"):
+ if d.allocated_amount:
+ remarks.append(_("Amount {0} {1} against {2} {3}").format(self.party_account_currency,
+ d.allocated_amount, d.reference_doctype, d.reference_name))
+
+ for d in self.get("deductions"):
+ if d.amount:
+ remarks.append(_("Amount {0} {1} deducted against {2}")
+ .format(self.company_currency, d.amount, d.account))
+
+ self.set("remarks", "\n".join(remarks))
+
+ def make_gl_entries(self, cancel=0, adv_adj=0):
+ gl_entries = []
+ self.add_party_gl_entries(gl_entries)
+ self.add_bank_gl_entries(gl_entries)
+ self.add_deductions_gl_entries(gl_entries)
+
+ make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
+
+ def add_party_gl_entries(self, gl_entries):
+ if self.party_account:
+ if self.payment_type=="Receive":
+ against_account = self.paid_to
+ else:
+ against_account = self.paid_from
+
+
+ party_gl_dict = self.get_gl_dict({
+ "account": self.party_account,
+ "party_type": self.party_type,
+ "party": self.party,
+ "against": against_account,
+ "account_currency": self.party_account_currency
+ })
+
+ dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
+
+ for d in self.get("references"):
+ gle = party_gl_dict.copy()
+ gle.update({
+ "against_voucher_type": d.reference_doctype,
+ "against_voucher": d.reference_name
+ })
+
+ allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
+ self.precision("paid_amount"))
+
+ gle.update({
+ dr_or_cr + "_in_account_currency": d.allocated_amount,
+ dr_or_cr: allocated_amount_in_company_currency
+ })
+
+ gl_entries.append(gle)
+
+ if self.unallocated_amount:
+ base_unallocated_amount = base_unallocated_amount = self.unallocated_amount * \
+ (self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
+
+ gle = party_gl_dict.copy()
+
+ gle.update({
+ dr_or_cr + "_in_account_currency": d.unallocated_amount,
+ dr_or_cr: base_unallocated_amount
+ })
+
+ gl_entries.append(gle)
+
+ def add_bank_gl_entries(self, gl_entries):
+ if self.payment_type in ("Pay", "Internal Transfer"):
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.paid_from,
+ "account_currency": self.paid_from_account_currency,
+ "against": self.party if self.payment_type=="Pay" else self.paid_to,
+ "credit_in_account_currency": self.paid_amount,
+ "credit": self.base_paid_amount
+ })
+ )
+ if self.payment_type in ("Receive", "Internal Transfer"):
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.paid_to,
+ "account_currency": self.paid_to_account_currency,
+ "against": self.party if self.payment_type=="Receive" else self.paid_from,
+ "debit_in_account_currency": self.received_amount,
+ "debit": self.base_received_amount
+ })
+ )
+
+ def add_deductions_gl_entries(self, gl_entries):
+ for d in self.get("deductions"):
+ if d.amount:
+ account_currency = get_account_currency(d.account)
+ if account_currency != self.company_currency:
+ frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
+
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": d.account,
+ "account_currency": account_currency,
+ "against": self.party or self.paid_from,
+ "debit_in_account_currency": d.amount,
+ "debit": d.amount,
+ "cost_center": d.cost_center
+ })
+ )
+
+ def update_advance_paid(self):
+ if self.payment_type in ("Receive", "Pay") and self.party:
+ for d in self.get("references"):
+ if d.allocated_amount and d.reference_doctype in ("Sales Order", "Purchase Order"):
+ frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
+
+@frappe.whitelist()
+def get_outstanding_reference_documents(args):
+ args = json.loads(args)
+
+ party_account_currency = get_account_currency(args.get("party_account"))
+ company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency")
+
+ # Get negative outstanding sales /purchase invoices
+ total_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
+
+ negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
+ args.get("party"), args.get("party_account"), total_field)
+
+ # Get positive outstanding sales /purchase invoices
+ outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
+ args.get("party_account"))
+
+ for d in outstanding_invoices:
+ d["exchange_rate"] = 1
+ if party_account_currency != company_currency \
+ and d.voucher_type in ("Sales Invoice", "Purchase Invoice"):
+ d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
+
+ # Get all SO / PO which are not fully billed or aginst which full advance not paid
+ orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party"),
+ party_account_currency, company_currency)
+
+ return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
+
+def get_orders_to_be_billed(party_type, party, party_account_currency, company_currency):
+ voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order'
+
+ ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
+
+ orders = frappe.db.sql("""
+ select
+ name as voucher_no,
+ {ref_field} as invoice_amount,
+ ({ref_field} - advance_paid) as outstanding_amount,
+ transaction_date as posting_date
+ from
+ `tab{voucher_type}`
+ where
+ {party_type} = %s
+ and docstatus = 1
+ and ifnull(status, "") != "Closed"
+ and {ref_field} > advance_paid
+ and abs(100 - per_billed) > 0.01
+ order by
+ transaction_date, name
+ """.format(**{
+ "ref_field": ref_field,
+ "voucher_type": voucher_type,
+ "party_type": scrub(party_type)
+ }), party, as_dict = True)
+
+ order_list = []
+ for d in orders:
+ d["voucher_type"] = voucher_type
+ d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency)
+ order_list.append(d)
+
+ return order_list
+
+def get_negative_outstanding_invoices(party_type, party, party_account, total_field):
+ voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
+ return frappe.db.sql("""
+ select
+ "{voucher_type}" as voucher_type, name as voucher_no,
+ {total_field} as invoice_amount, outstanding_amount, posting_date,
+ due_date, conversion_rate as exchange_rate
+ from
+ `tab{voucher_type}`
+ where
+ {party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0
+ order by
+ posting_date, name
+ """.format(**{
+ "total_field": total_field,
+ "voucher_type": voucher_type,
+ "party_type": scrub(party_type),
+ "party_account": "debit_to" if party_type=="Customer" else "credit_to"
+ }), (party, party_account), as_dict = True)
+
+@frappe.whitelist()
+def get_party_details(company, party_type, party, date):
+ if not frappe.db.exists(party_type, party):
+ frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
+
+ party_account = get_party_account(party_type, party, company)
+
+ account_currency = get_account_currency(party_account)
+ account_balance = get_balance_on(party_account, date)
+ party_balance = get_balance_on(party_type=party_type, party=party)
+
+ return {
+ "party_account": party_account,
+ "party_account_currency": account_currency,
+ "party_balance": party_balance,
+ "account_balance": account_balance
+ }
+
+@frappe.whitelist()
+def get_account_details(account, date):
+ return frappe._dict({
+ "account_currency": get_account_currency(account),
+ "account_balance": get_balance_on(account, date),
+ "account_type": frappe.db.get_value("Account", account, "account_type")
+ })
+
+@frappe.whitelist()
+def get_company_defaults(company):
+ fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
+ ret = frappe.db.get_value("Company", company, fields, as_dict=1)
+
+ for fieldname in fields:
+ if not ret[fieldname]:
+ frappe.throw(_("Please set default {0} in Company {1}")
+ .format(frappe.get_meta("Company").get_label(fieldname), company))
+
+ return ret
+
+@frappe.whitelist()
+def get_reference_details(reference_doctype, reference_name, party_account_currency):
+ total_amount = outstanding_amount = exchange_rate = None
+
+ if reference_doctype != "Journal Entry":
+ ref_doc = frappe.get_doc(reference_doctype, reference_name)
+
+ if party_account_currency == ref_doc.company_currency:
+ total_amount = ref_doc.base_grand_total
+ exchange_rate = 1
+ else:
+ total_amount = ref_doc.grand_total
+ exchange_rate = ref_doc.get("conversion_rate") or \
+ get_exchange_rate(party_account_currency, ref_doc.company_currency)
+
+ outstanding_amount = ref_doc.get("outstanding_amount") \
+ if reference_doctype in ("Sales Invoice", "Purchase Invoice") \
+ else flt(total_amount) - flt(ref_doc.advance_paid)
+ else:
+ exchange_rate = get_exchange_rate(party_account_currency, ref_doc.company_currency)
+
+ return frappe._dict({
+ "due_date": ref_doc.get("due_date"),
+ "total_amount": total_amount,
+ "outstanding_amount": outstanding_amount,
+ "exchange_rate": exchange_rate
+ })
+
+@frappe.whitelist()
+def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
+ doc = frappe.get_doc(dt, dn)
+
+ if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
+ frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
+
+ party_type = "Customer" if dt in ("Sales Invoice", "Sales Order") else "Supplier"
+
+ # party account
+ if dt == "Sales Invoice":
+ party_account = doc.debit_to
+ elif dt == "Purchase Invoice":
+ party_account = doc.credit_to
+ else:
+ party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
+
+ party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
+
+ # payment type
+ if (dt == "Sales Order" or (dt=="Sales Invoice" and doc.outstanding_amount > 0)) \
+ or (dt=="Purchase Invoice" and doc.outstanding_amount < 0):
+ payment_type = "Receive"
+ else:
+ payment_type = "Pay"
+
+ # amounts
+ grand_total = outstanding_amount = 0
+ if party_amount:
+ grand_total = outstanding_amount = party_amount
+ elif dt in ("Sales Invoice", "Purchase Invoice"):
+ grand_total = doc.grand_total
+ outstanding_amount = doc.outstanding_amount
+ else:
+ total_field = "base_grand_total" if party_account_currency == doc.company_currency else "grand_total"
+ grand_total = flt(doc.get(total_field))
+ outstanding_amount = grand_total - flt(doc.advance_paid)
+
+ # bank or cash
+ bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"),
+ account=bank_account)
+
+ paid_amount = received_amount = 0
+ if party_account_currency == bank.account_currency:
+ paid_amount = received_amount = abs(outstanding_amount)
+ elif payment_type == "Receive":
+ paid_amount = abs(outstanding_amount)
+ if bank_amount:
+ received_amount = bank_amount
+ else:
+ received_amount = abs(outstanding_amount)
+ if bank_amount:
+ paid_amount = bank_amount
+
+ pe = frappe.new_doc("Payment Entry")
+ pe.payment_type = payment_type
+ pe.company = doc.company
+ pe.posting_date = nowdate()
+ pe.mode_of_payment = doc.get("mode_of_payment")
+ pe.party_type = party_type
+ pe.party = doc.get(scrub(party_type))
+ pe.paid_from = party_account if payment_type=="Receive" else bank.account
+ pe.paid_to = party_account if payment_type=="Pay" else bank.account
+ pe.paid_from_account_currency = party_account_currency \
+ if payment_type=="Receive" else bank.account_currency
+ pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency
+ pe.paid_amount = paid_amount
+ pe.received_amount = received_amount
+
+ pe.append("references", {
+ "reference_doctype": dt,
+ "reference_name": dn,
+ "due_date": doc.get("due_date"),
+ "total_amount": grand_total,
+ "outstanding_amount": outstanding_amount,
+ "allocated_amount": outstanding_amount
+ })
+
+ pe.setup_party_account_field()
+ pe.set_missing_values()
+ pe.set_exchange_rate()
+ pe.set_amounts()
+ return pe
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
new file mode 100644
index 0000000..8be5f6d
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -0,0 +1,218 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from frappe.utils import flt, nowdate
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, InvalidPaymentEntry
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+
+test_dependencies = ["Item"]
+
+class TestPaymentEntry(unittest.TestCase):
+ def test_payment_entry_against_order(self):
+ so = make_sales_order()
+ pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Cash - _TC")
+ pe.paid_from = "Debtors - _TC"
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["Debtors - _TC", 0, 1000, so.name],
+ ["_Test Cash - _TC", 1000.0, 0, None]
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
+ self.assertEqual(so_advance_paid, 1000)
+
+ pe.cancel()
+
+ self.assertFalse(self.get_gle(pe.name))
+
+ so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
+ self.assertEqual(so_advance_paid, 0)
+
+ def test_payment_entry_against_si_usd_to_usd(self):
+ si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
+ currency="USD", conversion_rate=50)
+ pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
+ pe.reference_no = "1"
+ pe.reference_date = "2016-01-01"
+ pe.target_exchange_rate = 50
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Receivable USD - _TC", 0, 5000, si.name],
+ ["_Test Bank USD - _TC", 5000.0, 0, None]
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 0)
+
+ pe.cancel()
+ self.assertFalse(self.get_gle(pe.name))
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 100)
+
+ def test_payment_entry_against_pi(self):
+ pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
+ currency="USD", conversion_rate=50)
+ pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
+ pe.reference_no = "1"
+ pe.reference_date = "2016-01-01"
+ pe.source_exchange_rate = 50
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Payable USD - _TC", 12500, 0, pi.name],
+ ["_Test Bank USD - _TC", 0, 12500, None]
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 0)
+
+ def test_payment_entry_against_si_usd_to_inr(self):
+ si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
+ currency="USD", conversion_rate=50)
+ pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
+ bank_account="_Test Bank - _TC", bank_amount=900)
+ pe.reference_no = "1"
+ pe.reference_date = "2016-01-01"
+
+ self.assertEqual(pe.difference_amount, 100)
+
+ pe.append("deductions", {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": 100
+ })
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Receivable USD - _TC", 0, 1000, si.name],
+ ["_Test Bank - _TC", 900, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 100.0, 0, None],
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 80)
+
+ def test_internal_transfer_usd_to_inr(self):
+ pe = frappe.new_doc("Payment Entry")
+ pe.payment_type = "Internal Transfer"
+ pe.company = "_Test Company"
+ pe.paid_from = "_Test Bank USD - _TC"
+ pe.paid_to = "_Test Bank - _TC"
+ pe.paid_amount = 100
+ pe.source_exchange_rate = 50
+ pe.received_amount = 4500
+ pe.reference_no = "2"
+ pe.reference_date = nowdate()
+
+ pe.setup_party_account_field()
+ pe.set_missing_values()
+ pe.set_exchange_rate()
+ pe.set_amounts()
+
+ self.assertEquals(pe.difference_amount, 500)
+
+ pe.append("deductions", {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": 500
+ })
+
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Bank USD - _TC", 0, 5000, None],
+ ["_Test Bank - _TC", 4500, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 500.0, 0, None],
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ def test_payment_against_negative_sales_invoice(self):
+ pe1 = frappe.new_doc("Payment Entry")
+ pe1.payment_type = "Pay"
+ pe1.company = "_Test Company"
+ pe1.party_type = "Customer"
+ pe1.party = "_Test Customer"
+ pe1.paid_from = "_Test Cash - _TC"
+ pe1.paid_amount = 100
+ pe1.received_amount = 100
+
+ self.assertRaises(InvalidPaymentEntry, pe1.validate)
+
+ si1 = create_sales_invoice()
+
+ # create full payment entry against si1
+ pe2 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC")
+ pe2.insert()
+ pe2.submit()
+
+ # create return entry against si1
+ create_sales_invoice(is_return=1, return_against=si1.name, qty=-1)
+ si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")
+ self.assertEqual(si1_outstanding, -100)
+
+ # pay more than outstanding against si1
+ pe3 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC")
+ pe3.paid_amount = pe3.received_amount = 300
+ self.assertRaises(InvalidPaymentEntry, pe3.validate)
+
+ # pay negative outstanding against si1
+ pe3.paid_to = "Debtors - _TC"
+ pe3.paid_amount = pe3.received_amount = 100
+
+ pe3.insert()
+ pe3.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["Debtors - _TC", 100, 0, si1.name],
+ ["_Test Cash - _TC", 0, 100, None]
+ ])
+
+ self.validate_gl_entries(pe3.name, expected_gle)
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 0)
+
+ pe3.cancel()
+ self.assertFalse(self.get_gle(pe3.name))
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, -100)
+
+ def validate_gl_entries(self, voucher_no, expected_gle):
+ gl_entries = self.get_gle(voucher_no)
+
+ self.assertTrue(gl_entries)
+
+ for i, gle in enumerate(gl_entries):
+ self.assertEquals(expected_gle[gle.account][0], gle.account)
+ self.assertEquals(expected_gle[gle.account][1], gle.debit)
+ self.assertEquals(expected_gle[gle.account][2], gle.credit)
+ self.assertEquals(expected_gle[gle.account][3], gle.against_voucher)
+
+ def get_gle(self, voucher_no):
+ return frappe.db.sql("""select account, debit, credit, against_voucher
+ from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
+ order by account asc""", voucher_no, as_dict=1)
diff --git a/erpnext/accounts/doctype/payment_entry_deduction/__init__.py b/erpnext/accounts/doctype/payment_entry_deduction/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_deduction/__init__.py
diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
new file mode 100644
index 0000000..e71d973
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
@@ -0,0 +1,113 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-06-15 15:56:30.815503",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Cost Center",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cost Center",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2016-06-27 11:18:35.021945",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Entry Deduction",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
new file mode 100644
index 0000000..d6686bb
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class PaymentEntryDeduction(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_entry_reference/__init__.py b/erpnext/accounts/doctype/payment_entry_reference/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_reference/__init__.py
diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
new file mode 100644
index 0000000..5d61996
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
@@ -0,0 +1,237 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-06-01 16:55:32.196722",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "reference_doctype",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "due_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Due Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "total_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Total Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "outstanding_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Outstanding Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "allocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Allocated Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "exchange_rate",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Exchange Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2016-06-27 11:18:20.442383",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Entry Reference",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
new file mode 100644
index 0000000..51f8c06
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class PaymentEntryReference(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 5075f15..a50ff19 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -4,7 +4,15 @@
frappe.provide("erpnext.accounts");
erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
-
+ setup: function() {
+ this.frm.get_field('payments').grid.editable_fields = [
+ {fieldname: 'reference_name', columns: 3},
+ {fieldname: 'amount', columns: 2},
+ {fieldname: 'invoice_number', columns: 3},
+ {fieldname: 'allocated_amount', columns: 3}
+ ];
+ },
+
onload: function() {
var me = this
this.frm.set_query('party_type', function() {
@@ -105,14 +113,16 @@
if (row.invoice_number && !inList(invoices, row.invoice_number))
invoices.push(row.invoice_type + " | " + row.invoice_number);
});
+
+ if (invoices) {
+ frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
+ me.frm.doc.name).options = "\n" + invoices.join("\n");
- frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
- me.frm.doc.name).options = invoices.join("\n");
-
- $.each(me.frm.doc.payments || [], function(i, p) {
- if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null;
- });
-
+ $.each(me.frm.doc.payments || [], function(i, p) {
+ if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null;
+ });
+ }
+
refresh_field("payments");
},
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 832a346..5910a24 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -7,31 +7,49 @@
from frappe import msgprint, _
from frappe.model.document import Document
from erpnext.accounts.utils import get_outstanding_invoices
+from erpnext.controllers.accounts_controller import get_advance_payment_entries
class PaymentReconciliation(Document):
def get_unreconciled_entries(self):
- self.get_jv_entries()
+ self.get_nonreconciled_payment_entries()
self.get_invoice_entries()
+
+ def get_nonreconciled_payment_entries(self):
+ self.check_mandatory_to_fetch()
+
+ payment_entries = self.get_payment_entries()
+ journal_entries = self.get_jv_entries()
+
+ self.add_payment_entries(payment_entries + journal_entries)
+
+ def get_payment_entries(self):
+ order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
+ payment_entries = get_advance_payment_entries(self.party_type, self.party,
+ self.receivable_payable_account, order_doctype, against_all_orders=True)
+
+ return payment_entries
def get_jv_entries(self):
- self.check_mandatory_to_fetch()
dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
else "debit_in_account_currency"
bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
if self.bank_cash_account else "1=1"
- jv_entries = frappe.db.sql("""
+ journal_entries = frappe.db.sql("""
select
- t1.name as voucher_no, t1.posting_date, t1.remark,
- t2.name as voucher_detail_no, {dr_or_cr} as payment_amount, t2.is_advance
+ "Journal Entry" as reference_type, t1.name as reference_name,
+ t1.posting_date, t1.remark as remarks, t2.name as reference_row,
+ {dr_or_cr} as amount, t2.is_advance
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
where
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
and t2.party_type = %(party_type)s and t2.party = %(party)s
and t2.account = %(account)s and {dr_or_cr} > 0
- and (t2.reference_type is null or t2.reference_type in ('', 'Sales Order', 'Purchase Order'))
+ and (t2.reference_type is null or t2.reference_type = '' or
+ (t2.reference_type in ('Sales Order', 'Purchase Order')
+ and t2.reference_name is not null and t2.reference_name != ''))
and (CASE
WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
THEN 1=1
@@ -47,18 +65,13 @@
"bank_cash_account": "%%%s%%" % self.bank_cash_account
}, as_dict=1)
- self.add_payment_entries(jv_entries)
+ return list(journal_entries)
- def add_payment_entries(self, jv_entries):
+ def add_payment_entries(self, entries):
self.set('payments', [])
- for e in jv_entries:
- ent = self.append('payments', {})
- ent.journal_entry = e.get('voucher_no')
- ent.posting_date = e.get('posting_date')
- ent.amount = flt(e.get('payment_amount'))
- ent.remark = e.get('remark')
- ent.voucher_detail_number = e.get('voucher_detail_no')
- ent.is_advance = e.get('is_advance')
+ for e in entries:
+ row = self.append('payments', {})
+ row.update(e)
def get_invoice_entries(self):
#Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
@@ -90,14 +103,16 @@
self.get_invoice_entries()
self.validate_invoice()
- dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
- else "debit_in_account_currency"
+ dr_or_cr = "credit_in_account_currency" \
+ if self.party_type == "Customer" else "debit_in_account_currency"
+
lst = []
for e in self.get('payments'):
if e.invoice_number and e.allocated_amount:
- lst.append({
- 'voucher_no' : e.journal_entry,
- 'voucher_detail_no' : e.voucher_detail_number,
+ lst.append(frappe._dict({
+ 'voucher_type': e.reference_type,
+ 'voucher_no' : e.reference_name,
+ 'voucher_detail_no' : e.reference_row,
'against_voucher_type' : e.invoice_type,
'against_voucher' : e.invoice_number,
'account' : self.receivable_payable_account,
@@ -105,13 +120,14 @@
'party': self.party,
'is_advance' : e.is_advance,
'dr_or_cr' : dr_or_cr,
- 'unadjusted_amt' : flt(e.amount),
- 'allocated_amt' : flt(e.allocated_amount)
- })
-
+ 'unadjusted_amount' : flt(e.amount),
+ 'allocated_amount' : flt(e.allocated_amount)
+ }))
+
if lst:
from erpnext.accounts.utils import reconcile_against_document
reconcile_against_document(lst)
+
msgprint(_("Successfully Reconciled"))
self.get_unreconciled_entries()
@@ -142,7 +158,7 @@
.format(p.invoice_type, p.invoice_number))
if flt(p.allocated_amount) > flt(p.amount):
- frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to JV amount {2}")
+ frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2}")
.format(p.idx, p.allocated_amount, p.amount))
invoice_outstanding = unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
index f9b6039..401d234 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2014-07-09 16:13:35.452759",
"custom": 0,
"docstatus": 0,
@@ -12,18 +13,46 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "journal_entry",
+ "fieldname": "reference_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
- "label": "Journal Entry",
+ "in_list_view": 0,
+ "label": "Reference Type",
"length": 0,
"no_copy": 0,
- "options": "Journal Entry",
+ "options": "DocType",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Reference_name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "reference_type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -39,6 +68,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Posting Date",
@@ -46,6 +76,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -61,6 +92,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -68,6 +100,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -83,6 +116,7 @@
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Advance",
@@ -90,6 +124,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -101,17 +136,19 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "voucher_detail_number",
+ "fieldname": "reference_row",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Voucher Detail Number",
+ "label": "Reference Row",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -127,6 +164,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
@@ -134,6 +172,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -149,6 +188,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Invoice Number",
@@ -157,6 +197,7 @@
"options": "",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -172,6 +213,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Allocated amount",
@@ -180,6 +222,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -195,6 +238,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
@@ -202,6 +246,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -217,6 +262,7 @@
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Remark",
@@ -224,6 +270,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -234,21 +281,27 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:51.563989",
+ "menu_index": 0,
+ "modified": "2016-06-27 18:27:15.663498",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Payment",
"name_case": "",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "track_seen": 0,
+ "version": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index fc40d42..f7d9586 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -6,11 +6,11 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import flt, nowdate, get_url, cstr
+from frappe.utils import flt, get_url, nowdate
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import get_account_currency
-from erpnext.accounts.doctype.journal_entry.journal_entry import (get_payment_entry_against_invoice,
-get_payment_entry_against_order)
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, get_company_defaults
class PaymentRequest(Document):
def validate(self):
@@ -41,7 +41,6 @@
def on_submit(self):
send_mail = True
self.make_communication_entry()
-
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart":
@@ -77,55 +76,56 @@
if frappe.session.user == "Guest":
frappe.set_user("Administrator")
- jv = self.create_journal_entry()
+ payment_entry = self.create_payment_entry()
self.make_invoice()
- return jv
+ return payment_entry
- def create_journal_entry(self):
+ def create_payment_entry(self):
"""create entry"""
frappe.flags.ignore_account_permission = True
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
-
- party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company)
- party_account_currency = get_account_currency(party_account)
-
- debit_in_account_currency = 0.0
-
- if party_account_currency == ref_doc.company_currency:
- amount = flt(flt(self.grand_total) * \
- flt(ref_doc.conversion_rate, ref_doc.precision("conversion_rate")), \
- ref_doc.precision("base_grand_total"))
-
- if self.currency != ref_doc.company_currency:
- debit_in_account_currency = self.grand_total
-
- else:
- amount = debit_in_account_currency = self.grand_total
-
- if self.reference_doctype == "Sales Order":
- jv = get_payment_entry_against_order(self.reference_doctype, self.reference_name,
- amount=amount, debit_in_account_currency=debit_in_account_currency , journal_entry=True,
- bank_account=self.payment_account)
-
+
if self.reference_doctype == "Sales Invoice":
- jv = get_payment_entry_against_invoice(self.reference_doctype, self.reference_name,
- amount=amount, debit_in_account_currency=debit_in_account_currency, journal_entry=True,
- bank_account=self.payment_account)
+ party_account = ref_doc.debit_to
+ elif self.reference_doctype == "Purchase Invoice":
+ party_account = ref_doc.credit_to
+ else:
+ party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company)
- jv.update({
- "voucher_type": "Journal Entry",
- "posting_date": nowdate()
- })
+ party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(party_account)
- jv.insert(ignore_permissions=True)
- jv.submit()
+ bank_amount = self.grand_total
+ if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
+ party_amount = self.base_grand_total
+ else:
+ party_amount = self.grand_total
+
+ payment_entry = get_payment_entry(self.reference_doctype, self.reference_name,
+ party_amount=party_amount, bank_account=self.payment_account, bank_amount=bank_amount)
+
+ payment_entry.update({
+ "reference_no": self.name,
+ "reference_date": nowdate(),
+ "remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(self.reference_doctype,
+ self.reference_name, self.name)
+ })
+
+ company_details = get_company_defaults(ref_doc.company)
+ if payment_entry.difference_amount:
+ payment_entry.append("deductions", {
+ "account": company_details.exchange_gain_loss_account,
+ "cost_center": company_details.cost_center,
+ "amount": payment_entry.difference_amount
+ })
+ payment_entry.insert(ignore_permissions=True)
+ payment_entry.submit()
#set status as paid for Payment Request
frappe.db.set_value(self.doctype, self.name, "status", "Paid")
- return jv
+ return payment_entry
def send_email(self):
"""send email with payment link"""
@@ -177,7 +177,7 @@
grand_total = get_amount(ref_doc, args.dt)
existing_payment_request = frappe.db.get_value("Payment Request",
- {"reference_doctype": args.dt, "reference_name": args.dn})
+ {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ["!=", 2]})
if existing_payment_request:
pr = frappe.get_doc("Payment Request", existing_payment_request)
@@ -199,7 +199,6 @@
if args.return_doc:
return pr
-
if args.submit_doc:
pr.insert(ignore_permissions=True)
pr.submit()
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py
index eb8e558..73c412f 100644
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py
@@ -6,7 +6,7 @@
import frappe
import unittest
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request, get_gateway_details
+from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.setup.utils import get_exchange_rate
# test_records = frappe.get_test_records('Payment Request')
@@ -62,27 +62,44 @@
self.assertEquals(pr.currency, "USD")
def test_payment_entry(self):
+ frappe.db.set_value("Company", "_Test Company",
+ "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC")
+ frappe.db.set_value("Company", "_Test Company",
+ "write_off_account", "_Test Write Off - _TC")
+ frappe.db.set_value("Company", "_Test Company",
+ "cost_center", "_Test Cost Center - _TC")
+
so_inr = make_sales_order(currency="INR")
pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
mute_email=1, submit_doc=1)
- jv = pr.set_as_paid()
+ pe = pr.set_as_paid()
so_inr = frappe.get_doc("Sales Order", so_inr.name)
- self.assertEquals(so_inr.advance_paid, jv.total_debit)
-
- conversion_rate = get_exchange_rate("USD", "INR")
+ self.assertEquals(so_inr.advance_paid, 1000)
si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
- currency="USD", conversion_rate=conversion_rate)
+ currency="USD", conversion_rate=50)
pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
- mute_email=1, return_doc=1, payment_gateway="_Test Gateway - USD")
+ mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1)
+
+ pe = pr.set_as_paid()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Receivable USD - _TC", 0, 5000, si_usd.name],
+ [pr.payment_account, 6000.0, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 0, 1000, None]
+ ])
+
+ gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher
+ from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
+ order by account asc""", pe.name, as_dict=1)
- jv = pr.set_as_paid()
+ self.assertTrue(gl_entries)
- payment_gateway_details = get_gateway_details({"payment_gateway": "_Test Gateway - USD"})
-
- self.assertEquals(jv.accounts[0].account, "_Test Receivable USD - _TC")
- self.assertEquals(jv.accounts[1].account, payment_gateway_details.payment_account)
-
\ No newline at end of file
+ for i, gle in enumerate(gl_entries):
+ self.assertEquals(expected_gle[gle.account][0], gle.account)
+ self.assertEquals(expected_gle[gle.account][1], gle.debit)
+ self.assertEquals(expected_gle[gle.account][2], gle.credit)
+ self.assertEquals(expected_gle[gle.account][3], gle.against_voucher)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 7a83c7b..8aae29e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -34,8 +34,8 @@
}
if(!doc.is_return && doc.docstatus==1) {
- if(doc.outstanding_amount > 0) {
- this.frm.add_custom_button(__('Payment'), this.make_bank_entry, __("Make"));
+ if(doc.outstanding_amount != 0) {
+ this.frm.add_custom_button(__('Payment'), this.make_payment_entry, __("Make"));
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
}
@@ -218,21 +218,6 @@
}
}
-cur_frm.cscript.make_bank_entry = function() {
- return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
- args: {
- "dt": "Purchase Invoice",
- "dn": cur_frm.doc.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
-}
-
-
cur_frm.fields_dict['supplier_address'].get_query = function(doc, cdt, cdn) {
return{
filters:{'supplier': doc.supplier}
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 0826150..b247e3e 100755
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -2055,7 +2055,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "get_advances_paid",
+ "fieldname": "get_advances",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -2066,7 +2066,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Button",
- "options": "get_advances",
+ "options": "",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@@ -2366,6 +2366,31 @@
{
"allow_on_submit": 0,
"bold": 0,
+ "collapsible": 0,
+ "fieldname": "language",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Print Language",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
"collapsible": 1,
"fieldname": "more_info",
"fieldtype": "Section Break",
@@ -3006,7 +3031,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-06-08 17:30:39.470490",
+ "modified": "2016-06-30 13:40:39.440648",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index de65485..3bb402d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -65,7 +65,7 @@
self.po_required()
self.pr_required()
self.validate_supplier_invoice()
- self.validate_advance_jv("Purchase Order")
+
# validate cash purchase
if (self.is_paid == 1):
@@ -109,11 +109,6 @@
super(PurchaseInvoice, self).set_missing_values(for_validate)
- def get_advances(self):
- if not self.is_return:
- super(PurchaseInvoice, self).get_advances(self.credit_to, "Supplier", self.supplier,
- "Purchase Invoice Advance", "advances", "debit_in_account_currency", "purchase_order")
-
def check_conversion_rate(self):
default_currency = get_company_currency(self.company)
if not default_currency:
@@ -234,37 +229,6 @@
if not submitted:
frappe.throw(_("Purchase Receipt {0} is not submitted").format(d.purchase_receipt))
-
- def update_against_document_in_jv(self):
- """
- Links invoice and advance voucher:
- 1. cancel advance voucher
- 2. split into multiple rows if partially adjusted, assign against voucher
- 3. submit advance voucher
- """
-
- lst = []
- for d in self.get('advances'):
- if flt(d.allocated_amount) > 0:
- args = {
- 'voucher_no' : d.journal_entry,
- 'voucher_detail_no' : d.jv_detail_no,
- 'against_voucher_type' : 'Purchase Invoice',
- 'against_voucher' : self.name,
- 'account' : self.credit_to,
- 'party_type': 'Supplier',
- 'party': self.supplier,
- 'is_advance' : 'Yes',
- 'dr_or_cr' : 'debit_in_account_currency',
- 'unadjusted_amt' : flt(d.advance_amount),
- 'allocated_amt' : flt(d.allocated_amount)
- }
- lst.append(args)
-
- if lst:
- from erpnext.accounts.utils import reconcile_against_document
- reconcile_against_document(lst)
-
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.extend([{
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 5dacb9b..bb489e7 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -196,8 +196,9 @@
pi = frappe.copy_doc(test_records[0])
pi.append("advances", {
- "journal_entry": jv.name,
- "jv_detail_no": jv.get("accounts")[0].name,
+ "reference_type": "Journal Entry",
+ "reference_name": jv.name,
+ "reference_row": jv.get("accounts")[0].name,
"advance_amount": 400,
"allocated_amount": 300,
"remarks": jv.remark
diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
index 34156f5..462773d 100644
--- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
+++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
@@ -2,29 +2,33 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-03-08 15:36:46",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "journal_entry",
+ "fieldname": "reference_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
- "label": "Journal Entry",
+ "in_list_view": 0,
+ "label": "Reference Type",
"length": 0,
"no_copy": 1,
"oldfieldname": "journal_voucher",
"oldfieldtype": "Link",
- "options": "Journal Entry",
+ "options": "DocType",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "180px",
"read_only": 1,
"report_hide": 0,
@@ -38,19 +42,75 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "jv_detail_no",
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Reference Name",
+ "length": 0,
+ "no_copy": 1,
+ "options": "reference_type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Remarks",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "remarks",
+ "oldfieldtype": "Small Text",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": "150px",
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0,
+ "width": "150px"
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reference_row",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Journal Entry Detail No",
+ "label": "Reference Row",
"length": 0,
"no_copy": 1,
"oldfieldname": "jv_detail_no",
"oldfieldtype": "Date",
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"print_width": "80px",
"read_only": 1,
"report_hide": 0,
@@ -64,42 +124,18 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "remarks",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Remarks",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "remarks",
- "oldfieldtype": "Small Text",
- "permlevel": 0,
- "print_hide": 0,
- "print_width": "150px",
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0,
- "width": "150px"
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
"fieldname": "col_break1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -115,6 +151,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Advance Amount",
@@ -125,6 +162,7 @@
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 1,
"report_hide": 0,
@@ -142,6 +180,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Allocated Amount",
@@ -152,6 +191,7 @@
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 0,
"report_hide": 0,
@@ -165,18 +205,23 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:53.288895",
+ "menu_index": 0,
+ "modified": "2016-06-27 12:37:42.845967",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Advance",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "track_seen": 0,
+ "version": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index d64a024..4bc634a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -65,10 +65,14 @@
}
}
+ if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
+ cur_frm.add_custom_button(__('Payment'), this.make_payment_entry, __("Make"));
+ }
+
if(doc.outstanding_amount>0 && !cint(doc.is_return)) {
cur_frm.add_custom_button(__('Payment Request'), this.make_payment_request, __("Make"));
- cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry, __("Make"));
}
+
}
@@ -244,7 +248,7 @@
// Hide Fields
// ------------
cur_frm.cscript.hide_fields = function(doc) {
- parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances_received',
+ parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) {
@@ -280,20 +284,6 @@
})
}
-cur_frm.cscript.make_bank_entry = function() {
- return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
- args: {
- "dt": "Sales Invoice",
- "dn": cur_frm.doc.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
-}
-
cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
return {
filters: [
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index c68c262..746209f 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -1068,9 +1068,9 @@
{
"allow_on_submit": 0,
"bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": "",
- "depends_on": "eval:doc.total_billing_amount > 0",
+ "collapsible": 1,
+ "collapsible_depends_on": "eval:doc.total_billing_amount > 0",
+ "depends_on": "",
"fieldname": "time_sheet_list",
"fieldtype": "Section Break",
"hidden": 0,
@@ -2019,7 +2019,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "get_advances_received",
+ "fieldname": "get_advances",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -2030,7 +2030,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Button",
- "options": "get_advances",
+ "options": "",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@@ -3676,7 +3676,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-06-27 15:13:36.523798",
+ "modified": "2016-07-02 20:10:14.146963",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 470ea91..82cb365 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -77,7 +77,6 @@
self.check_close_sales_order("sales_order")
self.validate_debit_to_acc()
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
- self.validate_advance_jv("Sales Order")
self.add_remarks()
self.validate_write_off_account()
self.validate_fixed_asset()
@@ -294,44 +293,9 @@
return pos
- def get_advances(self):
- if not self.is_return:
- super(SalesInvoice, self).get_advances(self.debit_to, "Customer", self.customer,
- "Sales Invoice Advance", "advances", "credit_in_account_currency", "sales_order")
-
def get_company_abbr(self):
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
- def update_against_document_in_jv(self):
- """
- Links invoice and advance voucher:
- 1. cancel advance voucher
- 2. split into multiple rows if partially adjusted, assign against voucher
- 3. submit advance voucher
- """
-
- lst = []
- for d in self.get('advances'):
- if flt(d.allocated_amount) > 0:
- args = {
- 'voucher_no' : d.journal_entry,
- 'voucher_detail_no' : d.jv_detail_no,
- 'against_voucher_type' : 'Sales Invoice',
- 'against_voucher' : self.name,
- 'account' : self.debit_to,
- 'party_type': 'Customer',
- 'party': self.customer,
- 'is_advance' : 'Yes',
- 'dr_or_cr' : 'credit_in_account_currency',
- 'unadjusted_amt' : flt(d.advance_amount),
- 'allocated_amt' : flt(d.allocated_amount)
- }
- lst.append(args)
-
- if lst:
- from erpnext.accounts.utils import reconcile_against_document
- reconcile_against_document(lst)
-
def validate_debit_to_acc(self):
account = frappe.db.get_value("Account", self.debit_to,
["account_type", "report_type", "account_currency"], as_dict=True)
@@ -740,7 +704,8 @@
account = frappe.db.get_value("Mode of Payment Account",
{"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))
+ frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
+ .format(mode_of_payment))
return {
"account": account
}
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index cc3d952..ec63b1f 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -676,8 +676,9 @@
si = frappe.copy_doc(test_records[0])
si.append("advances", {
"doctype": "Sales Invoice Advance",
- "journal_entry": jv.name,
- "jv_detail_no": jv.get("accounts")[0].name,
+ "reference_type": "Journal Entry",
+ "reference_name": jv.name,
+ "reference_row": jv.get("accounts")[0].name,
"advance_amount": 400,
"allocated_amount": 300,
"remarks": jv.remark
diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
index b1fae8e..06c6735 100644
--- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
+++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
@@ -2,29 +2,33 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-02-22 01:27:41",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "journal_entry",
+ "fieldname": "reference_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
- "label": "Journal Entry",
+ "in_list_view": 0,
+ "label": "Reference Type",
"length": 0,
"no_copy": 1,
"oldfieldname": "journal_voucher",
"oldfieldtype": "Link",
- "options": "Journal Entry",
+ "options": "DocType",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "250px",
"read_only": 1,
"report_hide": 0,
@@ -38,10 +42,37 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "remarks",
- "fieldtype": "Small Text",
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Reference Name",
+ "length": 0,
+ "no_copy": 1,
+ "options": "reference_type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Remarks",
@@ -51,8 +82,9 @@
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "150px",
- "read_only": 1,
+ "read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -64,19 +96,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "jv_detail_no",
+ "fieldname": "reference_row",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Journal Entry Detail No",
+ "label": "Reference Row",
"length": 0,
"no_copy": 1,
"oldfieldname": "jv_detail_no",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 1,
"report_hide": 0,
@@ -94,12 +128,14 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -115,6 +151,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Advance amount",
@@ -125,6 +162,7 @@
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 1,
"report_hide": 0,
@@ -142,6 +180,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Allocated amount",
@@ -152,6 +191,7 @@
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 0,
"report_hide": 0,
@@ -165,18 +205,24 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:56.263776",
+ "menu_index": 0,
+ "modified": "2016-06-27 12:38:58.845134",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Advance",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "DESC",
+ "track_seen": 0,
+ "version": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index b7fb053..4af197e 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -160,7 +160,6 @@
gl_map.append(round_off_gle)
-
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
adv_adj=False, update_outstanding="Yes"):
@@ -168,8 +167,12 @@
check_freezing_date, update_outstanding_amt, validate_frozen_account
if not gl_entries:
- gl_entries = frappe.db.sql("""select * from `tabGL Entry`
+ gl_entries = frappe.db.sql("""
+ select account, posting_date, party_type, party, cost_center, fiscal_year,
+ voucher_type, voucher_no, against_voucher_type, against_voucher, cost_center
+ from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
+
if gl_entries:
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
@@ -180,7 +183,7 @@
validate_frozen_account(entry["account"], adv_adj)
validate_balance_type(entry["account"], adv_adj)
validate_expense_against_budget(entry)
-
+
if entry.get("against_voucher") and update_outstanding == 'Yes':
update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"),
entry.get("against_voucher"), on_cancel=True)
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 8d11374..c65a845 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -181,8 +181,16 @@
{"parenttype": party_group_doctype, "parent": group, "company": company}, "account")
if not account:
- default_account_name = "default_receivable_account" if party_type=="Customer" else "default_payable_account"
+ default_account_name = "default_receivable_account" \
+ if party_type=="Customer" else "default_payable_account"
account = frappe.db.get_value("Company", company, default_account_name)
+
+ existing_gle_currency = get_party_gle_currency(party_type, party, company)
+ if existing_gle_currency:
+ if account:
+ account_currency = frappe.db.get_value("Account", account, "account_currency")
+ if (account and account_currency != existing_gle_currency) or not account:
+ account = get_party_gle_account(party_type, party, company)
return account
@@ -203,6 +211,17 @@
return frappe.local_cache("party_gle_currency", (party_type, party, company), generator,
regenerate_if_none=True)
+
+def get_party_gle_account(party_type, party, company):
+ def generator():
+ existing_gle_account = frappe.db.sql("""select account from `tabGL Entry`
+ where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
+ limit 1""", { "company": company, "party_type": party_type, "party": party })
+
+ return existing_gle_account[0][0] if existing_gle_account else None
+
+ return frappe.local_cache("party_gle_account", (party_type, party, company), generator,
+ regenerate_if_none=True)
def validate_party_gle_currency(party_type, party, company, party_account_currency=None):
"""Validate party account currency with existing GL Entry's currency"""
@@ -235,7 +254,7 @@
if doc.default_currency and party_account_currency and company_default_currency:
if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency:
- frappe.throw(_("Billing currency must be equal to either default comapany's currency or party's payble account currency"))
+ frappe.throw(_("Billing currency must be equal to either default comapany's currency or party account currency"))
@frappe.whitelist()
def get_due_date(posting_date, party_type, party, company):
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
index 37d8130..ac3366c 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
@@ -22,6 +22,7 @@
"fieldtype": "Link",
"options": "Account",
"reqd": 1,
+ "default": locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"],
"get_query": function() {
return {
"query": "erpnext.controllers.queries.get_account_list",
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
index cfb677f..07ab969 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -3,7 +3,8 @@
from __future__ import unicode_literals
import frappe
-from frappe import _, msgprint
+from frappe import _
+from frappe.utils import nowdate, getdate
def execute(filters=None):
if not filters: filters = {}
@@ -14,28 +15,36 @@
return columns, data
def get_columns():
- return [_("Journal Entry") + ":Link/Journal Entry:140", _("Account") + ":Link/Account:140",
- _("Posting Date") + ":Date:100", _("Clearance Date") + ":Date:110", _("Against Account") + ":Link/Account:200",
- _("Debit") + ":Currency:120", _("Credit") + ":Currency:120"
+ return [
+ _("Payment Document") + ":Link/DocType:130",
+ _("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":110",
+ _("Posting Date") + ":Date:100",
+ _("Cheque/Reference No") + "::120",
+ _("Clearance Date") + ":Date:100",
+ _("Against Account") + ":Link/Account:170",
+ _("Amount") + ":Currency:120"
]
def get_conditions(filters):
conditions = ""
- if not filters.get("account"):
- msgprint(_("Please select Bank Account"), raise_exception=1)
- else:
- conditions += " and jvd.account = %(account)s"
- if filters.get("from_date"): conditions += " and jv.posting_date>=%(from_date)s"
- if filters.get("to_date"): conditions += " and jv.posting_date<=%(to_date)s"
+ if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s"
+ if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s"
return conditions
def get_entries(filters):
conditions = get_conditions(filters)
- entries = frappe.db.sql("""select jv.name, jvd.account, jv.posting_date,
- jv.clearance_date, jvd.against_account, jvd.debit, jvd.credit
+ journal_entries = frappe.db.sql("""select "Journal Entry", jv.name, jv.posting_date,
+ jv.cheque_no, jv.clearance_date, jvd.against_account, (jvd.debit - jvd.credit)
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- where jvd.parent = jv.name and jv.docstatus=1 %s
- order by jv.name DESC""" % conditions, filters, as_list=1)
- return entries
\ No newline at end of file
+ where jvd.parent = jv.name and jv.docstatus=1 and jvd.account = %(account)s {0}
+ order by posting_date DESC, jv.name DESC""".format(conditions), filters, as_list=1)
+
+ payment_entries = frappe.db.sql("""select "Payment Entry", name, posting_date,
+ reference_no, clearance_date, party, if(paid_from=%(account)s, paid_amount, received_amount)
+ from `tabPayment Entry`
+ where docstatus=1 and (paid_from = %(account)s or paid_to = %(account)s) {0}
+ order by posting_date DESC, name DESC""".format(conditions), filters, as_list=1, debug=1)
+
+ return sorted(journal_entries + payment_entries, key=lambda k: k[2] or getdate(nowdate()))
\ No newline at end of file
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
index 581a7c8..76e0de2 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import flt
+from frappe.utils import flt, getdate, nowdate
from frappe import _
def execute(filters=None):
@@ -24,16 +24,8 @@
for d in data:
total_debit += flt(d.debit)
total_credit += flt(d.credit)
-
- amounts_not_reflected_in_system = frappe.db.sql("""
- select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
- from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%s
- and jv.posting_date > %s and jv.clearance_date <= %s and ifnull(jv.is_opening, 'No') = 'No'
- """, (filters["account"], filters["report_date"], filters["report_date"]))
-
- amounts_not_reflected_in_system = flt(amounts_not_reflected_in_system[0][0]) \
- if amounts_not_reflected_in_system else 0.0
+
+ amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
+ amounts_not_reflected_in_system
@@ -42,7 +34,7 @@
get_balance_row(_("Bank Statement balance as per General Ledger"), balance_as_per_system, account_currency),
{},
{
- "journal_entry": _("Outstanding Cheques and Deposits to clear"),
+ "payment_entry": _("Outstanding Cheques and Deposits to clear"),
"debit": total_debit,
"credit": total_credit,
"account_currency": account_currency
@@ -61,13 +53,21 @@
"fieldname": "posting_date",
"label": _("Posting Date"),
"fieldtype": "Date",
- "width": 100
+ "width": 90
},
{
- "fieldname": "journal_entry",
- "label": _("Journal Entry"),
+ "fieldname": "payment_document",
+ "label": _("Payment Document"),
"fieldtype": "Link",
- "options": "Journal Entry",
+ "options": "DocType",
+ "width": 120,
+ "hidden": 1
+ },
+ {
+ "fieldname": "payment_entry",
+ "label": _("Payment Entry"),
+ "fieldtype": "Dynamic Link",
+ "options": "payment_document",
"width": 220
},
{
@@ -92,7 +92,7 @@
"width": 200
},
{
- "fieldname": "reference",
+ "fieldname": "reference_no",
"label": _("Reference"),
"fieldtype": "Data",
"width": 100
@@ -119,31 +119,67 @@
]
def get_entries(filters):
- entries = frappe.db.sql("""select
- jv.posting_date, jv.name as journal_entry, jvd.debit_in_account_currency as debit,
+ journal_entries = frappe.db.sql("""
+ select "Journal Entry" as payment_document, jv.posting_date,
+ jv.name as payment_entry, jvd.debit_in_account_currency as debit,
jvd.credit_in_account_currency as credit, jvd.against_account,
- jv.cheque_no as reference, jv.cheque_date as ref_date, jv.clearance_date, jvd.account_currency
+ jv.cheque_no as reference_no, jv.cheque_date as ref_date, jv.clearance_date, jvd.account_currency
from
`tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
- and ifnull(jv.is_opening, 'No') = 'No'
- order by jv.posting_date DESC,jv.name DESC""", filters, as_dict=1)
+ and ifnull(jv.is_opening, 'No') = 'No'""", filters, as_dict=1)
+
+ payment_entries = frappe.db.sql("""
+ select
+ "Payment Entry" as payment_document, name as payment_entry,
+ reference_no, reference_date as ref_date,
+ if(paid_to=%(account)s, received_amount, 0) as debit,
+ if(paid_from=%(account)s, paid_amount, 0) as credit,
+ posting_date, party as against_account, clearance_date,
+ if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
+ from `tabPayment Entry`
+ where
+ (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
+ and posting_date <= %(report_date)s
+ and ifnull(clearance_date, '4000-01-01') > %(report_date)s
+ """, filters, as_dict=1)
- return entries
+ return sorted(list(payment_entries)+list(journal_entries),
+ key=lambda k: k['posting_date'] or getdate(nowdate()))
+
+def get_amounts_not_reflected_in_system(filters):
+ je_amount = frappe.db.sql("""
+ select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
+ from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
+ where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%(account)s
+ and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
+ and ifnull(jv.is_opening, 'No') = 'No' """, filters)
+
+ je_amount = flt(je_amount[0][0]) if je_amount else 0.0
+
+ pe_amount = frappe.db.sql("""
+ select sum(if(paid_from=%(account)s, paid_amount, received_amount))
+ from `tabPayment Entry`
+ where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
+ and posting_date > %(report_date)s and clearance_date <= %(report_date)s""", filters)
+
+ pe_amount = flt(pe_amount[0][0]) if pe_amount else 0.0
+
+ return je_amount + pe_amount
def get_balance_row(label, amount, account_currency):
if amount > 0:
return {
- "journal_entry": label,
+ "payment_entry": label,
"debit": amount,
"credit": 0,
"account_currency": account_currency
}
else:
return {
- "journal_entry": label,
+ "payment_entry": label,
"debit": 0,
"credit": abs(amount),
"account_currency": account_currency
diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
index 20bdae8..85c7daa 100644
--- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
+++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
@@ -5,7 +5,7 @@
import frappe
from frappe import _
from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data
-from frappe.utils import flt, getdate
+from frappe.utils import getdate, flt
def execute(filters=None):
if not filters: filters = {}
@@ -14,21 +14,21 @@
columns = get_columns(filters)
entries = get_entries(filters)
invoice_details = get_invoice_posting_date_map(filters)
- against_date = ""
data = []
for d in entries:
- invoice = invoice_details.get(d.reference_name) or frappe._dict()
+ invoice = invoice_details.get(d.against_voucher) or frappe._dict()
+
if d.reference_type=="Purchase Invoice":
payment_amount = flt(d.debit) or -1 * flt(d.credit)
else:
payment_amount = flt(d.credit) or -1 * flt(d.debit)
- row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name, invoice.posting_date,
- invoice.due_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
+ row = [d.voucher_type, d.voucher_no, d.party_type, d.party, d.posting_date, d.against_voucher,
+ invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks]
- if d.reference_name:
- row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount)
+ if d.against_voucher:
+ row += get_ageing_data(30, 60, 90, d.posting_date, invoice.posting_date, payment_amount)
else:
row += ["", "", "", "", ""]
if invoice.due_date:
@@ -46,7 +46,8 @@
def get_columns(filters):
return [
- _("Journal Entry") + ":Link/Journal Entry:140",
+ _("Payment Document") + ":Link/DocType: 100",
+ _("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":140",
_("Party Type") + "::100",
_("Party") + ":Dynamic Link/Party Type:140",
_("Posting Date") + ":Date:100",
@@ -55,8 +56,6 @@
_("Payment Due Date") + ":Date:130",
_("Debit") + ":Currency:120",
_("Credit") + ":Currency:120",
- _("Reference No") + "::100",
- _("Reference Date") + ":Date:100",
_("Remarks") + "::150",
_("Age") +":Int:40",
"0-30:Currency:100",
@@ -69,45 +68,36 @@
def get_conditions(filters):
conditions = []
- if not filters.get("party_type"):
- if filters.get("payment_type") == "Outgoing":
- filters["party_type"] = "Supplier"
+ if not filters.party_type:
+ if filters.payment_type == "Outgoing":
+ filters.party_type = "Supplier"
else:
- filters["party_type"] = "Customer"
+ filters.party_type = "Customer"
- if filters.get("party_type"):
- conditions.append("jvd.party_type=%(party_type)s")
+ if filters.party_type:
+ conditions.append("party_type=%(party_type)s")
- if filters.get("party"):
- conditions.append("jvd.party=%(party)s")
+ if filters.party:
+ conditions.append("party=%(party)s")
- if filters.get("party_type"):
- conditions.append("jvd.reference_type=%(reference_type)s")
- if filters.get("party_type") == "Customer":
- filters["reference_type"] = "Sales Invoice"
- else:
- filters["reference_type"] = "Purchase Invoice"
-
- if filters.get("company"):
- conditions.append("jv.company=%(company)s")
+ if filters.party_type:
+ conditions.append("against_voucher_type=%(reference_type)s")
+ filters["reference_type"] = "Sales Invoice" if filters.party_type=="Customer" else "Purchase Invoice"
if filters.get("from_date"):
- conditions.append("jv.posting_date >= %(from_date)s")
+ conditions.append("posting_date >= %(from_date)s")
+
if filters.get("to_date"):
- conditions.append("jv.posting_date <= %(to_date)s")
+ conditions.append("posting_date <= %(to_date)s")
- return "and {}".format(" and ".join(conditions)) if conditions else ""
+ return "and " + " and ".join(conditions) if conditions else ""
def get_entries(filters):
- conditions = get_conditions(filters)
- entries = frappe.db.sql("""select jv.name, jvd.party_type, jvd.party, jv.posting_date,
- jvd.reference_type, jvd.reference_name, jvd.debit, jvd.credit,
- jv.cheque_no, jv.cheque_date, jv.remark
- from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" %
- conditions, filters, as_dict=1)
-
- return entries
+ return frappe.db.sql("""select
+ voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher
+ from `tabGL Entry`
+ where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0}
+ """.format(get_conditions(filters)), filters, as_dict=1)
def get_invoice_posting_date_map(filters):
invoice_details = {}
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 27389aa..6bd5eb1 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -172,53 +172,77 @@
Cancel JV, Update aginst document, split if required and resubmit jv
"""
for d in args:
- check_if_jv_modified(d)
+
+ check_if_advance_entry_modified(d)
validate_allocated_amount(d)
+
+ # cancel advance entry
+ doc = frappe.get_doc(d.voucher_type, d.voucher_no)
- # cancel JV
- jv_obj = frappe.get_doc('Journal Entry', d['voucher_no'])
+ doc.make_gl_entries(cancel=1, adv_adj=1)
- jv_obj.make_gl_entries(cancel=1, adv_adj=1)
+ # update ref in advance entry
+ if d.voucher_type == "Journal Entry":
+ update_reference_in_journal_entry(d, doc)
+ else:
+ update_reference_in_payment_entry(d, doc)
- # update ref in JV Detail
- update_against_doc(d, jv_obj)
+ # re-submit advance entry
+ doc = frappe.get_doc(d.voucher_type, d.voucher_no)
+ doc.make_gl_entries(cancel = 0, adv_adj =1)
- # re-submit JV
- jv_obj = frappe.get_doc('Journal Entry', d['voucher_no'])
- jv_obj.make_gl_entries(cancel = 0, adv_adj =1)
-
-
-def check_if_jv_modified(args):
+def check_if_advance_entry_modified(args):
"""
check if there is already a voucher reference
check if amount is same
check if jv is submitted
"""
- ret = frappe.db.sql("""
- select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2
- where t1.name = t2.parent and t2.account = %(account)s
- and t2.party_type = %(party_type)s and t2.party = %(party)s
- and (t2.reference_type is null or t2.reference_type in ("", "Sales Order", "Purchase Order"))
- and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
- and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
+ ret = None
+ if args.voucher_type == "Journal Entry":
+ ret = frappe.db.sql("""
+ select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2
+ where t1.name = t2.parent and t2.account = %(account)s
+ and t2.party_type = %(party_type)s and t2.party = %(party)s
+ and (t2.reference_type is null or t2.reference_type in ("", "Sales Order", "Purchase Order"))
+ and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
+ and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
+ else:
+ party_account_field = "paid_from" if args.party_type == "Customer" else "paid_to"
+ if args.voucher_detail_no:
+ ret = frappe.db.sql("""select t1.name
+ from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
+ where
+ t1.name = t2.parent and t1.docstatus = 1
+ and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
+ and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s
+ and t2.reference_doctype in ("", "Sales Order", "Purchase Order")
+ and t2.allocated_amount = %(unadjusted_amount)s
+ """.format(party_account_field), args)
+ else:
+ ret = frappe.db.sql("""select name from `tabPayment Entry`
+ where
+ name = %(voucher_no)s and docstatus = 1
+ and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s
+ and unallocated_amount = %(unadjusted_amount)s
+ """.format(party_account_field), args)
if not ret:
throw(_("""Payment Entry has been modified after you pulled it. Please pull it again."""))
def validate_allocated_amount(args):
- if args.get("allocated_amt") < 0:
+ if args.get("allocated_amount") < 0:
throw(_("Allocated amount can not be negative"))
- elif args.get("allocated_amt") > args.get("unadjusted_amt"):
- throw(_("Allocated amount can not greater than unadusted amount"))
+ elif args.get("allocated_amount") > args.get("unadjusted_amount"):
+ throw(_("Allocated amount can not greater than unadjusted amount"))
-def update_against_doc(d, jv_obj):
+def update_reference_in_journal_entry(d, jv_obj):
"""
Updates against document, if partial amount splits into rows
"""
jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
- jv_detail.set(d["dr_or_cr"], d["allocated_amt"])
+ jv_detail.set(d["dr_or_cr"], d["allocated_amount"])
jv_detail.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit',
- d["allocated_amt"]*flt(jv_detail.exchange_rate))
+ d["allocated_amount"]*flt(jv_detail.exchange_rate))
original_reference_type = jv_detail.reference_type
original_reference_name = jv_detail.reference_name
@@ -226,14 +250,14 @@
jv_detail.set("reference_type", d["against_voucher_type"])
jv_detail.set("reference_name", d["against_voucher"])
- if d['allocated_amt'] < d['unadjusted_amt']:
+ if d['allocated_amount'] < d['unadjusted_amount']:
jvd = frappe.db.sql("""
select cost_center, balance, against_account, is_advance,
account_type, exchange_rate, account_currency
from `tabJournal Entry Account` where name = %s
""", d['voucher_detail_no'], as_dict=True)
- amount_in_account_currency = flt(d['unadjusted_amt']) - flt(d['allocated_amt'])
+ amount_in_account_currency = flt(d['unadjusted_amount']) - flt(d['allocated_amount'])
amount_in_company_currency = amount_in_account_currency * flt(jvd[0]['exchange_rate'])
# new entry with balance amount
@@ -263,6 +287,39 @@
# will work as update after submit
jv_obj.flags.ignore_validate_update_after_submit = True
jv_obj.save(ignore_permissions=True)
+
+def update_reference_in_payment_entry(d, payment_entry):
+ reference_details = {
+ "reference_doctype": d.against_voucher_type,
+ "reference_name": d.against_voucher,
+ "total_amount": d.grand_total,
+ "outstanding_amount": d.outstanding_amount,
+ "allocated_amount": d.allocated_amount,
+ "exchange_rate": d.exchange_rate
+ }
+
+ if d.voucher_detail_no:
+ existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
+ original_row = existing_row.as_dict().copy()
+ existing_row.update(reference_details)
+
+ if d.allocated_amount < original_row.allocated_amount:
+ new_row = payment_entry.append("references")
+ new_row.docstatus = 1
+ for field in reference_details.keys():
+ new_row.set(field, original_row[field])
+
+ new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount
+ else:
+ new_row = payment_entry.append("references")
+ new_row.docstatus = 1
+ new_row.update(reference_details)
+
+ payment_entry.flags.ignore_validate_update_after_submit = True
+ payment_entry.setup_party_account_field()
+ payment_entry.set_missing_values()
+ payment_entry.set_amounts()
+ payment_entry.save(ignore_permissions=True)
def remove_against_link_from_jv(ref_type, ref_no):
linked_jv = frappe.db.sql_list("""select parent from `tabJournal Entry Account`
@@ -290,7 +347,7 @@
value = frappe.db.get_value("Company", company, fieldname)
if not value:
- throw(_("Please set default value {0} in Company {1}").format(frappe.get_meta("Company").get_label(fieldname), company))
+ throw(_("Please set default {0} in Company {1}").format(frappe.get_meta("Company").get_label(fieldname), company))
return value
@@ -378,7 +435,7 @@
payment_dr_or_cr = "payment_gl_entry.debit_in_account_currency - payment_gl_entry.credit_in_account_currency"
invoice_list = frappe.db.sql("""select
- voucher_no, voucher_type, posting_date,
+ voucher_no, voucher_type, posting_date,
ifnull(sum({dr_or_cr}), 0) as invoice_amount,
(
select
@@ -401,11 +458,11 @@
and {dr_or_cr} > 0
{condition}
and ((voucher_type = 'Journal Entry'
- and (against_voucher = ''
- or against_voucher is null))
- or (voucher_type != 'Journal Entry'))
+ and (against_voucher = '' or against_voucher is null))
+ or (voucher_type not in ('Journal Entry', 'Payment Entry')))
group by voucher_type, voucher_no
- having (invoice_amount - payment_amount) > 0.005""".format(
+ having (invoice_amount - payment_amount) > 0.005
+ order by posting_date, name""".format(
dr_or_cr = dr_or_cr,
payment_dr_or_cr = payment_dr_or_cr,
condition = condition or ""
@@ -416,16 +473,18 @@
}, as_dict=True)
for d in invoice_list:
- outstanding_invoices.append({
+ outstanding_invoices.append(frappe._dict({
'voucher_no': d.voucher_no,
'voucher_type': d.voucher_type,
'posting_date': d.posting_date,
'invoice_amount': flt(d.invoice_amount),
'payment_amount': flt(d.payment_amount),
'outstanding_amount': flt(d.invoice_amount - d.payment_amount, precision),
- 'due_date': frappe.db.get_value(d.voucher_type, d.voucher_no, "due_date")
- })
-
+ 'due_date': frappe.db.get_value(d.voucher_type, d.voucher_no, "due_date"),
+ }))
+
+ outstanding_invoices = sorted(outstanding_invoices, key=lambda k: k['due_date'] or getdate(nowdate()))
+
return outstanding_invoices
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 9e44855..f5d4e2e 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -76,7 +76,7 @@
this.make_purchase_invoice, __("Make"));
if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
- cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry, __("Make"));
+ cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __("Make"));
}
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
@@ -184,20 +184,6 @@
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
},
- make_bank_entry: function() {
- return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order",
- args: {
- "dt": "Purchase Order",
- "dn": cur_frm.doc.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
- },
-
unclose_purchase_order: function(){
cur_frm.cscript.update_status('Re-open', 'Submitted')
},
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index d4b2221..ec1bf46 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -308,7 +308,7 @@
def postprocess(source, target):
set_missing_values(source, target)
#Get the advance paid Journal Entries in Purchase Invoice Advance
- target.get_advances()
+ target.set_advances()
def update_item(obj, target, source_parent):
target.amount = flt(obj.amount) - flt(obj.billed_amt)
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index aab006f..726f767 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -4,10 +4,10 @@
{% include 'erpnext/buying/doctype/purchase_common/purchase_common.js' %};
-
+cur_frm.add_fetch('contact', 'email_id', 'email_id')
frappe.ui.form.on("Request for Quotation",{
- setup: function(frm){
+ setup: function(frm) {
frm.fields_dict["suppliers"].grid.get_field("contact").get_query = function(doc, cdt, cdn){
var d =locals[cdt][cdn];
return {
@@ -21,15 +21,15 @@
{fieldname: 'schedule_date', columns: 2},
{fieldname: 'warehouse', columns: 3},
];
-
+
frm.get_field('suppliers').grid.editable_fields = [
- {fieldname: 'supplier', columns: 5},
+ {fieldname: 'supplier', columns: 4},
{fieldname: 'contact', columns: 3},
{fieldname: 'email_id', columns: 3}
];
},
- onload: function(frm){
+ onload: function(frm) {
frm.add_fetch('standard_reply', 'response', 'response');
if(!frm.doc.message_for_supplier) {
@@ -37,10 +37,14 @@
}
},
- refresh: function(frm, cdt, cdn){
+ refresh: function(frm, cdt, cdn) {
if (frm.doc.docstatus === 1) {
- frm.add_custom_button(__("Make Supplier Quotation"),
- function(){ frm.trigger("make_suppplier_quotation") });
+ frm.add_custom_button(__("Make"),
+ function(){ frm.trigger("make_suppplier_quotation") }, __("Supplier Quotation"));
+
+ frm.add_custom_button(__("View"),
+ function(){ frappe.set_route('List', 'Supplier Quotation',
+ {'request_for_quotation': frm.doc.name}) }, __("Supplier Quotation"));
frm.add_custom_button(__("Send Supplier Emails"), function() {
frappe.call({
@@ -54,7 +58,7 @@
}
},
- make_suppplier_quotation: function(frm){
+ make_suppplier_quotation: function(frm) {
var doc = frm.doc;
var dialog = new frappe.ui.Dialog({
title: __("For Supplier"),
@@ -93,10 +97,24 @@
})
frappe.ui.form.on("Request for Quotation Supplier",{
- supplier: function(frm, cdt, cdn){
+ supplier: function(frm, cdt, cdn) {
var d = locals[cdt][cdn]
frappe.model.set_value(cdt, cdn, 'contact', '')
frappe.model.set_value(cdt, cdn, 'email_id', '')
+ },
+
+ download_pdf: function(frm, cdt, cdn) {
+ var child = locals[cdt][cdn]
+
+ var w = window.open(
+ frappe.urllib.get_full_url("/api/method/erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_pdf?"
+ +"doctype="+encodeURIComponent(frm.doc.doctype)
+ +"&name="+encodeURIComponent(frm.doc.name)
+ +"&supplier_idx="+encodeURIComponent(child.idx)
+ +"&no_letterhead=0"));
+ if(!w) {
+ msgprint(__("Please enable pop-ups")); return;
+ }
}
})
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
index 7f1988a..2ce5c5c 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
@@ -3,6 +3,7 @@
"allow_import": 1,
"allow_rename": 0,
"autoname": "naming_series:",
+ "beta": 0,
"creation": "2016-02-25 01:24:07.224790",
"custom": 0,
"docstatus": 0,
@@ -70,6 +71,33 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "description": "For individual supplier",
+ "fieldname": "vendor",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Supplier",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Supplier",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
@@ -603,13 +631,14 @@
"hide_toolbar": 0,
"icon": "icon-shopping-cart",
"idx": 0,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-03-29 06:18:26.398938",
+ "modified": "2016-06-30 01:57:49.233065",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation",
@@ -757,6 +786,7 @@
"write": 0
}
],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 1,
"search_fields": "status, transaction_date",
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index b02c22e..eea796a 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -6,8 +6,11 @@
import frappe, json
from frappe import _
from frappe.model.mapper import get_mapped_doc
-from frappe.utils import get_url, random_string
+from frappe.utils import get_url, random_string, cint
from frappe.utils.user import get_user_fullname
+from frappe.utils.print_format import download_pdf
+from frappe.desk.form.load import get_attachments
+from frappe.core.doctype.communication.email import make
from erpnext.accounts.party import get_party_account_currency, get_party_details
from erpnext.stock.doctype.material_request.material_request import set_missing_values
from erpnext.controllers.buying_controller import BuyingController
@@ -34,6 +37,10 @@
if not rfq_supplier.email_id:
rfq_supplier.email_id = frappe.db.get_value("Contact", rfq_supplier.contact, "email_id")
+ def validate_email_id(self, args):
+ if not args.email_id:
+ frappe.throw(_("Row {0}: For supplier {0} email id is required to send email").format(args.idx, args.supplier))
+
def on_submit(self):
frappe.db.set(self, 'status', 'Submitted')
@@ -41,42 +48,61 @@
frappe.db.set(self, 'status', 'Cancelled')
def send_to_supplier(self):
- link = get_url("/rfq/" + self.name)
for rfq_supplier in self.suppliers:
- if rfq_supplier.email_id:
+ if rfq_supplier.send_email:
+ self.validate_email_id(rfq_supplier)
# make new user if required
- update_password_link = self.create_supplier_user(rfq_supplier, link)
+ update_password_link = self.update_supplier_contact(rfq_supplier, self.get_link())
- self.supplier_rfq_mail(rfq_supplier, update_password_link, link)
- else:
- frappe.throw(_("For supplier {0} email id is required to send email").format(rfq_supplier.supplier))
+ self.update_supplier_part_no(rfq_supplier)
+ self.supplier_rfq_mail(rfq_supplier, update_password_link, self.get_link())
- def create_supplier_user(self, rfq_supplier, link):
+ def get_link(self):
+ # RFQ link for supplier portal
+ return get_url("/rfq/" + self.name)
+
+ def update_supplier_part_no(self, args):
+ self.vendor = args.supplier
+ for item in self.items:
+ item.supplier_part_no = frappe.db.get_value('Item Supplier',
+ {'parent': item.item_code, 'supplier': args.supplier}, 'supplier_part_no')
+
+ def update_supplier_contact(self, rfq_supplier, link):
'''Create a new user for the supplier if not set in contact'''
update_password_link = ''
- contact = frappe.get_doc("Contact", rfq_supplier.contact)
- if not contact.user:
- if frappe.db.exists("User", rfq_supplier.email_id):
- user = frappe.get_doc("User", rfq_supplier.email_id)
- else:
- user, update_password_link = self.create_user(rfq_supplier, link)
+ if frappe.db.exists("User", rfq_supplier.email_id):
+ user = frappe.get_doc("User", rfq_supplier.email_id)
+ else:
+ user, update_password_link = self.create_user(rfq_supplier, link)
- # set user_id in contact
- contact = frappe.get_doc('Contact', rfq_supplier.contact)
- contact.user = user.name
- contact.save()
+ self.update_contact_of_supplier(rfq_supplier, user)
return update_password_link
+ def update_contact_of_supplier(self, rfq_supplier, user):
+ if rfq_supplier.contact:
+ contact = frappe.get_doc("Contact", rfq_supplier.contact)
+ else:
+ contact = frappe.new_doc("Contact")
+ contact.first_name = rfq_supplier.supplier_name or rfq_supplier.supplier
+ contact.supplier = rfq_supplier.supplier
+
+ if not contact.email_id and not contact.user:
+ contact.email_id = user.name
+ contact.user = user.name
+
+ contact.save(ignore_permissions=True)
+
def create_user(self, rfq_supplier, link):
user = frappe.get_doc({
'doctype': 'User',
'send_welcome_email': 0,
'email': rfq_supplier.email_id,
- 'first_name': rfq_supplier.supplier_name,
- 'user_type': 'Website User'
+ 'first_name': rfq_supplier.supplier_name or rfq_supplier.supplier,
+ 'user_type': 'Website User',
+ 'redirect_url': link
})
user.save(ignore_permissions=True)
update_password_link = user.reset_password()
@@ -98,25 +124,40 @@
subject = _("Request for Quotation")
template = "templates/emails/request_for_quotation.html"
sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None
+ message = frappe.get_template(template).render(args)
+ attachments = self.get_attachments()
- frappe.sendmail(recipients=data.email_id, sender=sender, subject=subject,
- message=frappe.get_template(template).render(args),
- attachments = [frappe.attach_print('Request for Quotation', self.name)])
+ self.send_email(data, sender, subject, message, attachments)
+
+ def send_email(self, data, sender, subject, message, attachments):
+ make(subject = subject, content=message,recipients=data.email_id,
+ sender=sender,attachments = attachments, send_email=True)["name"]
+
frappe.msgprint(_("Email sent to supplier {0}").format(data.supplier))
+ def get_attachments(self):
+ attachments = [d.name for d in get_attachments(self.doctype, self.name)]
+ attachments.append(frappe.attach_print(self.doctype, self.name, doc=self))
+ return attachments
+
@frappe.whitelist()
def send_supplier_emails(rfq_name):
+ check_portal_enabled('Request for Quotation')
rfq = frappe.get_doc("Request for Quotation", rfq_name)
if rfq.docstatus==1:
rfq.send_to_supplier()
+def check_portal_enabled(reference_doctype):
+ if not frappe.db.get_value('Portal Menu Item',
+ {'reference_doctype': reference_doctype}, 'enabled'):
+ frappe.throw(_("Request for Quotation is disabled to access from portal, for more check portal settings."))
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)
+ list_context["show_sidebar"] = True
return list_context
-
# This method is used to make supplier quotation from material request form.
@frappe.whitelist()
def make_supplier_quotation(source_name, for_supplier, target_doc=None):
@@ -136,11 +177,10 @@
},
"Request for Quotation Item": {
"doctype": "Supplier Quotation Item",
- "field_map": [
- ["name", "request_for_quotation_item"],
- ["parent", "request_for_quotation"],
- ["uom", "uom"]
- ],
+ "field_map": {
+ "name": "request_for_quotation_item",
+ "parent": "request_for_quotation"
+ },
}
}, target_doc, postprocess)
@@ -190,3 +230,16 @@
"request_for_quotation_item": data.name,
"request_for_quotation": data.parent
})
+
+@frappe.whitelist()
+def get_pdf(doctype, name, supplier_idx):
+ doc = get_rfq_doc(doctype, name, supplier_idx)
+ if doc:
+ download_pdf(doctype, name, doc=doc)
+
+def get_rfq_doc(doctype, name, supplier_idx):
+ if cint(supplier_idx):
+ doc = frappe.get_doc(doctype, name)
+ args = doc.get('suppliers')[cint(supplier_idx) - 1]
+ doc.update_supplier_part_no(args)
+ return doc
\ No newline at end of file
diff --git a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json
index 7a23f43..af3e265 100644
--- a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json
+++ b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json
@@ -42,6 +42,31 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "fieldname": "supplier_part_no",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Supplier Part No",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@@ -444,11 +469,11 @@
"in_list_view": 0,
"label": "Material Request",
"length": 0,
- "no_copy": 1,
+ "no_copy": 0,
"options": "Material Request",
"permlevel": 0,
"precision": "",
- "print_hide": 0,
+ "print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
@@ -494,7 +519,7 @@
"in_list_view": 0,
"label": "Material Request Item",
"length": 0,
- "no_copy": 1,
+ "no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
@@ -601,8 +626,8 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-06-17 12:25:37.655901",
- "modified_by": "umair@erpnext.com",
+ "modified": "2016-07-01 01:07:59.541478",
+ "modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation Item",
"name_case": "",
diff --git a/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json b/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
index 3399b51..bfd5ec1 100644
--- a/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
+++ b/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
@@ -10,6 +10,32 @@
"document_type": "",
"fields": [
{
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "default": "1",
+ "fieldname": "send_email",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Send Email",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -136,18 +162,44 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "download_pdf",
+ "fieldtype": "Button",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Download PDF",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-05-31 19:08:15.397706",
+ "modified": "2016-06-30 12:47:23.564189",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation Supplier",
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index 9a3d58c..a4ee326 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -8,7 +8,7 @@
from frappe.model.naming import make_autoname
from erpnext.utilities.address_and_contact import load_address_and_contact
from erpnext.utilities.transaction_base import TransactionBase
-from erpnext.accounts.party import validate_party_accounts, get_timeline_data
+from erpnext.accounts.party import validate_party_accounts, get_timeline_data # keep this
from erpnext.accounts.party_status import get_party_status
class Supplier(TransactionBase):
@@ -18,7 +18,6 @@
def onload(self):
"""Load address and contacts in `__onload`"""
load_address_and_contact(self, "supplier")
- self.set_onload('links', self.meta.get_links_setup())
def autoname(self):
supp_master_name = frappe.defaults.get_global_default('supp_master_name')
@@ -83,15 +82,4 @@
def update_supplier_address(self, newdn, set_field):
frappe.db.sql("""update `tabAddress` set address_title=%(newdn)s
{set_field} where supplier=%(newdn)s"""\
- .format(set_field=set_field), ({"newdn": newdn}))
-
-@frappe.whitelist()
-def get_dashboard_data(name):
- '''load dashboard related data'''
- frappe.has_permission(doc=frappe.get_doc('Supplier', name), throw=True)
-
- from frappe.desk.notifications import get_open_count
- return {
- 'count': get_open_count('Supplier', name),
- 'timeline_data': get_timeline_data('Supplier', name),
- }
+ .format(set_field=set_field), ({"newdn": newdn}))
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
index 1275fcd..6ddc728 100644
--- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
+++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
@@ -3,6 +3,7 @@
"allow_import": 0,
"allow_rename": 0,
"autoname": "hash",
+ "beta": 0,
"creation": "2013-05-22 12:43:10",
"custom": 0,
"docstatus": 0,
@@ -854,16 +855,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "prevdoc_docname",
+ "fieldname": "material_request",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
- "label": "Material Request No",
+ "label": "Material Request",
"length": 0,
- "no_copy": 1,
+ "no_copy": 0,
"oldfieldname": "prevdoc_docname",
"oldfieldtype": "Link",
"options": "Material Request",
@@ -932,16 +933,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "prevdoc_detail_docname",
+ "fieldname": "material_request_item",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
- "label": "Material Request Detail No",
+ "label": "Material Request Item",
"length": 0,
- "no_copy": 1,
+ "no_copy": 0,
"oldfieldname": "prevdoc_detail_docname",
"oldfieldtype": "Data",
"permlevel": 0,
@@ -1091,20 +1092,23 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-03-25 17:01:59.632826",
+ "modified": "2016-07-01 01:07:29.202856",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation Item",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py
index b6b1480..9dd50f8 100644
--- a/erpnext/config/accounts.py
+++ b/erpnext/config/accounts.py
@@ -46,6 +46,11 @@
},
{
"type": "doctype",
+ "name": "Payment Entry",
+ "description": _("Payment entries against party or for internal transfer")
+ },
+ {
+ "type": "doctype",
"name": "Journal Entry",
"description": _("Accounting journal entries.")
},
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index e3a66a4..2b67ceb 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -42,6 +42,7 @@
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
self.validate_due_date()
+ self.validate_advance_entries()
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
@@ -255,7 +256,7 @@
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
- if self.doctype not in ["Journal Entry", "Period Closing Voucher"]:
+ if self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
self.validate_account_currency(gl_dict.account, account_currency)
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency)
@@ -276,71 +277,114 @@
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
and allocated_amount = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
- def get_advances(self, account_head, party_type, party, child_doctype, parentfield, dr_or_cr, against_order_field):
+ def set_advances(self):
"""Returns list of advances against Account, Party, Reference"""
- order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
+
+ res = self.get_advance_entries()
- # conver sales_order to "Sales Order"
- reference_type = against_order_field.replace("_", " ").title()
-
- condition = ""
- if order_list:
- in_placeholder = ', '.join(['%s'] * len(order_list))
- condition = "or (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\
- .format(reference_type, in_placeholder)
-
- res = frappe.db.sql("""
- select
- t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no,
- t2.reference_name as against_order
- from
- `tabJournal Entry` t1, `tabJournal Entry Account` t2
- where
- t1.name = t2.parent and t2.account = %s
- and t2.party_type = %s and t2.party = %s
- and t2.is_advance = 'Yes' and t1.docstatus = 1
- and (ifnull(t2.reference_type, '')='' {1})
- order by t1.posting_date""".format(dr_or_cr, condition),
- [account_head, party_type, party] + order_list, as_dict=1)
-
- self.set(parentfield, [])
+ self.set("advances", [])
for d in res:
- self.append(parentfield, {
- "doctype": child_doctype,
- "journal_entry": d.jv_no,
- "jv_detail_no": d.jv_detail_no,
- "remarks": d.remark,
+ self.append("advances", {
+ "doctype": self.doctype + " Advance",
+ "reference_type": d.reference_type,
+ "reference_name": d.reference_name,
+ "reference_row": d.reference_row,
+ "remarks": d.remarks,
"advance_amount": flt(d.amount),
"allocated_amount": flt(d.amount) if d.against_order else 0
})
+
+ def get_advance_entries(self, include_unallocated=True):
+ if self.doctype == "Sales Invoice":
+ party_account = self.debit_to
+ party_type = "Customer"
+ party = self.customer
+ amount_field = "credit_in_account_currency"
+ order_field = "sales_order"
+ order_doctype = "Sales Order"
+ else:
+ party_account = self.credit_to
+ party_type = "Supplier"
+ party = self.supplier
+ amount_field = "debit_in_account_currency"
+ order_field = "purchase_order"
+ order_doctype = "Purchase Order"
+
+ order_list = list(set([d.get(order_field)
+ for d in self.get("items") if d.get(order_field)]))
+
+ journal_entries = get_advance_journal_entries(party_type, party, party_account,
+ amount_field, order_doctype, order_list, include_unallocated)
+
+ payment_entries = get_advance_payment_entries(party_type, party, party_account,
+ order_doctype, order_list, include_unallocated)
+
+ res = journal_entries + payment_entries
+
+ return res
- def validate_advance_jv(self, reference_type):
- against_order_field = frappe.scrub(reference_type)
- order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
- if order_list:
- account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
+ def validate_advance_entries(self):
+ order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order"
+ order_list = list(set([d.get(order_field)
+ for d in self.get("items") if d.get(order_field)]))
+
+ if not order_list: return
+
+ advance_entries = self.get_advance_entries(include_unallocated=False)
+
+ if advance_entries:
+ advance_entries_against_si = [d.reference_name for d in self.get("advances")]
+ for d in advance_entries:
+ if not advance_entries_against_si or d.reference_name not in advance_entries_against_si:
+ frappe.msgprint(_("Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
+ .format(d.reference_name, d.against_order))
+
+ def update_against_document_in_jv(self):
+ """
+ Links invoice and advance voucher:
+ 1. cancel advance voucher
+ 2. split into multiple rows if partially adjusted, assign against voucher
+ 3. submit advance voucher
+ """
+
+ if self.doctype == "Sales Invoice":
+ party_type = "Customer"
+ party = self.customer
+ party_account = self.debit_to
+ dr_or_cr = "credit_in_account_currency"
+ else:
+ party_type = "Supplier"
+ party = self.supplier
+ party_account = self.credit_to
+ dr_or_cr = "debit_in_account_currency"
- jv_against_order = frappe.db.sql("""select parent, reference_name as against_order
- from `tabJournal Entry Account`
- where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
- and reference_type=%s
- and ifnull(reference_name, '') in ({0})
- group by parent, reference_name""".format(', '.join(['%s']*len(order_list))),
- tuple([account, reference_type] + order_list), as_dict=1)
-
- if jv_against_order:
- order_jv_map = {}
- for d in jv_against_order:
- order_jv_map.setdefault(d.against_order, []).append(d.parent)
-
- advance_jv_against_si = [d.journal_entry for d in self.get("advances")]
-
- for order, jv_list in order_jv_map.items():
- for jv in jv_list:
- if not advance_jv_against_si or jv not in advance_jv_against_si:
- frappe.msgprint(_("Journal Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
- .format(jv, order))
-
+ lst = []
+ for d in self.get('advances'):
+ if flt(d.allocated_amount) > 0:
+ args = frappe._dict({
+ 'voucher_type': d.reference_type,
+ 'voucher_no' : d.reference_name,
+ 'voucher_detail_no' : d.reference_row,
+ 'against_voucher_type' : self.doctype,
+ 'against_voucher' : self.name,
+ 'account' : party_account,
+ 'party_type': party_type,
+ 'party': party,
+ 'is_advance' : 'Yes',
+ 'dr_or_cr' : dr_or_cr,
+ 'unadjusted_amount' : flt(d.advance_amount),
+ 'allocated_amount' : flt(d.allocated_amount),
+ 'exchange_rate': (self.conversion_rate
+ if self.party_account_currency != self.company_currency else 1),
+ 'grand_total': (self.base_grand_total
+ if self.party_account_currency==self.company_currency else self.grand_total),
+ 'outstanding_amount': self.outstanding_amount
+ })
+ lst.append(args)
+
+ if lst:
+ from erpnext.accounts.utils import reconcile_against_document
+ reconcile_against_document(lst)
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_tolerance_for
@@ -396,10 +440,10 @@
select
account_currency, sum({dr_or_cr}) as amount
from
- `tabJournal Entry Account`
+ `tabGL Entry`
where
- reference_type = %s and reference_name = %s and party=%s
- and docstatus = 1 and is_advance = "Yes"
+ against_voucher_type = %s and against_voucher = %s and party=%s
+ and docstatus = 1
""".format(dr_or_cr=dr_or_cr), (self.doctype, self.name, party), as_dict=1)
if advance:
@@ -420,11 +464,11 @@
formatted_order_total = fmt_money(order_total, precision=self.precision("base_grand_total"),
currency=advance.account_currency)
- if order_total >= advance_paid:
- frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
- else:
+ if self.currency == self.company_currency and advance_paid > order_total:
frappe.throw(_("Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2})")
.format(formatted_advance_paid, self.name, formatted_order_total))
+
+ frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
@property
def company_abbr(self):
@@ -600,3 +644,77 @@
if flt(gl_dict.credit) and not flt(gl_dict.credit_in_account_currency):
gl_dict.credit_in_account_currency = gl_dict.credit if account_currency==company_currency \
else flt(gl_dict.credit / conversion_rate, 2)
+
+
+def get_advance_journal_entries(party_type, party, party_account, amount_field,
+ order_doctype, order_list, include_unallocated=True):
+
+ dr_or_cr = "credit_in_account_currency" if party_type=="Customer" else "debit_in_account_currency"
+
+ conditions = []
+ if include_unallocated:
+ conditions.append("ifnull(t2.reference_name, '')=''")
+
+ if order_list:
+ order_condition = ', '.join(['%s'] * len(order_list))
+ conditions.append(" (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\
+ .format(order_doctype, order_condition))
+
+ reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else ""
+
+ journal_entries = frappe.db.sql("""
+ select
+ "Journal Entry" as reference_type, t1.name as reference_name,
+ t1.remark as remarks, t2.{0} as amount, t2.name as reference_row,
+ t2.reference_name as against_order
+ from
+ `tabJournal Entry` t1, `tabJournal Entry Account` t2
+ where
+ t1.name = t2.parent and t2.account = %s
+ and t2.party_type = %s and t2.party = %s
+ and t2.is_advance = 'Yes' and t1.docstatus = 1
+ and {1} > 0
+ and (ifnull(t2.reference_name, '')='' {2})
+ order by t1.posting_date""".format(amount_field, dr_or_cr, reference_condition),
+ [party_account, party_type, party] + order_list, as_dict=1)
+
+ return list(journal_entries)
+
+def get_advance_payment_entries(party_type, party, party_account,
+ order_doctype, order_list=None, include_unallocated=True, against_all_orders=False):
+ party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
+ payment_type = "Receive" if party_type == "Customer" else "Pay"
+ payment_entries_against_order, unallocated_payment_entries = [], []
+
+ if order_list or against_all_orders:
+ if order_list:
+ reference_condition = " and t2.reference_name in ({0})"\
+ .format(', '.join(['%s'] * len(order_list)))
+ else:
+ reference_condition = ""
+ order_list = []
+
+ payment_entries_against_order = frappe.db.sql("""
+ select
+ "Payment Entry" as reference_type, t1.name as reference_name,
+ t1.remarks, t2.allocated_amount as amount, t2.name as reference_row,
+ t2.reference_name as against_order, t1.posting_date
+ from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
+ where
+ t1.name = t2.parent and t1.{0} = %s and t1.payment_type = %s
+ and t1.party_type = %s and t1.party = %s and t1.docstatus = 1
+ and t2.reference_doctype = %s {1}
+ """.format(party_account_field, reference_condition),
+ [party_account, payment_type, party_type, party, order_doctype] + order_list, as_dict=1)
+
+ if include_unallocated:
+ unallocated_payment_entries = frappe.db.sql("""
+ select "Payment Entry" as reference_type, name as reference_name,
+ remarks, unallocated_amount as amount
+ from `tabPayment Entry`
+ where
+ {0} = %s and party_type = %s and party = %s and payment_type = %s
+ and docstatus = 1 and unallocated_amount > 0
+ """.format(party_account_field), (party_account, party_type, party, payment_type), as_dict=1)
+
+ return list(payment_entries_against_order) + list(unallocated_payment_entries)
\ No newline at end of file
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 8f687d1..7661925 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -32,29 +32,29 @@
self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items()
self.validate_warehouse()
-
+
if self.doctype=="Purchase Invoice":
self.validate_purchase_receipt_if_update_stock()
-
+
if self.doctype=="Purchase Receipt" or (self.doctype=="Purchase Invoice" and self.update_stock):
self.validate_purchase_return()
self.validate_rejected_warehouse()
self.validate_accepted_rejected_qty()
-
+
pc_obj = frappe.get_doc('Purchase Common')
pc_obj.validate_for_items(self)
-
+
#sub-contracting
self.validate_for_subcontracting()
self.create_raw_materials_supplied("supplied_items")
self.set_landed_cost_voucher_amount()
-
+
if self.doctype in ("Purchase Receipt", "Purchase Invoice"):
self.update_valuation_rate("items")
def set_missing_values(self, for_validate=False):
super(BuyingController, self).set_missing_values(for_validate)
-
+
self.set_supplier_from_item_default()
self.set_price_list_currency("Buying")
@@ -85,7 +85,7 @@
from `tabLanded Cost Item`
where docstatus = 1 and purchase_receipt_item = %s""", d.name)
d.landed_cost_voucher_amount = lc_voucher_amount[0][0] if lc_voucher_amount else 0.0
-
+
def set_total_in_words(self):
from frappe.utils import money_in_words
company_currency = get_company_currency(self.company)
@@ -286,7 +286,7 @@
if not d.conversion_factor:
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
-
+
def validate_purchase_return(self):
for d in self.get("items"):
if self.is_return and flt(d.rejected_qty) != 0:
@@ -299,7 +299,7 @@
if flt(d.rejected_qty) and not d.rejected_warehouse:
if self.rejected_warehouse:
d.rejected_warehouse = self.rejected_warehouse
-
+
if not d.rejected_warehouse:
frappe.throw(_("Row #{0}: Rejected Warehouse is mandatory against rejected Item {1}").format(d.idx, d.item_code))
@@ -318,10 +318,10 @@
# Check Received Qty = Accepted Qty + Rejected Qty
if ((flt(d.qty) + flt(d.rejected_qty)) != flt(d.received_qty)):
frappe.throw(_("Accepted + Rejected Qty must be equal to Received quantity for Item {0}").format(d.item_code))
-
+
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
self.update_ordered_qty()
-
+
sl_entries = []
stock_items = self.get_stock_items()
@@ -335,10 +335,10 @@
"serial_no": cstr(d.serial_no).strip()
})
if self.is_return:
- original_incoming_rate = frappe.db.get_value("Stock Ledger Entry",
- {"voucher_type": "Purchase Receipt", "voucher_no": self.return_against,
+ original_incoming_rate = frappe.db.get_value("Stock Ledger Entry",
+ {"voucher_type": "Purchase Receipt", "voucher_no": self.return_against,
"item_code": d.item_code}, "incoming_rate")
-
+
sle.update({
"outgoing_rate": original_incoming_rate
})
@@ -361,14 +361,14 @@
self.make_sl_entries_for_supplier_warehouse(sl_entries)
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock,
via_landed_cost_voucher=via_landed_cost_voucher)
-
+
def update_ordered_qty(self):
po_map = {}
for d in self.get("items"):
if self.doctype=="Purchase Receipt" \
and d.prevdoc_doctype=="Purchase Order" and d.prevdoc_detail_docname:
po_map.setdefault(d.prevdoc_docname, []).append(d.prevdoc_detail_docname)
-
+
elif self.doctype=="Purchase Invoice" and d.purchase_order and d.po_detail:
po_map.setdefault(d.purchase_order, []).append(d.po_detail)
@@ -381,7 +381,7 @@
frappe.InvalidStatusError)
po_obj.update_ordered_qty(po_item_rows)
-
+
def make_sl_entries_for_supplier_warehouse(self, sl_entries):
if hasattr(self, 'supplied_items'):
for d in self.get('supplied_items'):
@@ -392,4 +392,4 @@
"warehouse": self.supplier_warehouse,
"actual_qty": -1*flt(d.consumed_qty),
}))
-
+
diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py
index 30f9172..75c6e08 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -90,7 +90,7 @@
doc.set_indicator()
doc.status_display = ", ".join(doc.status_display)
- doc.items_preview = ", ".join([d.item_name for d in doc.items])
+ doc.items_preview = ", ".join([d.item_name for d in doc.items if d.item_name])
result.append(doc)
return result
diff --git a/erpnext/demo/__init__.py b/erpnext/demo/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/demo/__init__.py
diff --git a/erpnext/demo/data/account.json b/erpnext/demo/data/account.json
new file mode 100644
index 0000000..b50b0c9
--- /dev/null
+++ b/erpnext/demo/data/account.json
@@ -0,0 +1,18 @@
+[{
+ "account_name": "Debtors EUR",
+ "parent_account": "Accounts Receivable",
+ "account_type": "Receivable",
+ "account_currency": "EUR"
+},
+{
+ "account_name": "Creditors EUR",
+ "parent_account": "Accounts Payable",
+ "account_type": "Payable",
+ "account_currency": "EUR"
+},
+{
+ "account_name": "Paypal",
+ "parent_account": "Bank Accounts",
+ "account_type": "Bank",
+ "account_currency": "EUR"
+}]
\ No newline at end of file
diff --git a/erpnext/demo/data/address.json b/erpnext/demo/data/address.json
new file mode 100644
index 0000000..be4d5ec
--- /dev/null
+++ b/erpnext/demo/data/address.json
@@ -0,0 +1,245 @@
+[
+ {
+ "address_line1": "254 Theotokopoulou Str.",
+ "address_type": "Office",
+ "city": "Larnaka",
+ "country": "Cyprus",
+ "customer": "Adaptas",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "R Patr\u00e3o Caramelho 116",
+ "address_type": "Office",
+ "city": "Fajozes",
+ "country": "Portugal",
+ "customer": "Asian Fusion",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "30 Fulford Road",
+ "address_type": "Office",
+ "city": "PENTRE-PIOD",
+ "country": "United Kingdom",
+ "customer": "Asian Junction",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "Schoenebergerstrasse 13",
+ "address_type": "Office",
+ "city": "Raschau",
+ "country": "Germany",
+ "customer": "Big D Supermarkets",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "Hoheluftchaussee 43",
+ "address_type": "Office",
+ "city": "Kieritzsch",
+ "country": "Germany",
+ "customer": "Buttrey Food & Drug",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "R Cimo Vila 6",
+ "address_type": "Office",
+ "city": "Rebordosa",
+ "country": "Portugal",
+ "customer": "Chi-Chis",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "R 5 Outubro 9",
+ "address_type": "Office",
+ "city": "Quinta Nova S\u00e3o Domingos",
+ "country": "Portugal",
+ "customer": "Choices",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "Avenida Macambira 953",
+ "address_type": "Office",
+ "city": "Goi\u00e2nia",
+ "country": "Brazil",
+ "customer": "Consumers and Consumers Express",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "2342 Goyeau Ave",
+ "address_type": "Office",
+ "city": "Windsor",
+ "country": "Canada",
+ "customer": "Crafts Canada",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "Laukaantie 82",
+ "address_type": "Office",
+ "city": "KOKKOLA",
+ "country": "Finland",
+ "customer": "Endicott Shoes",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "9 Brown Street",
+ "address_type": "Office",
+ "city": "PETERSHAM",
+ "country": "Australia",
+ "customer": "Fayva",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "Via Donnalbina 41",
+ "address_type": "Office",
+ "city": "Cala Gonone",
+ "country": "Italy",
+ "customer": "Intelacard",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "Liljerum Grenadj\u00e4rtorpet 69",
+ "address_type": "Office",
+ "city": "TOMTEBODA",
+ "country": "Sweden",
+ "customer": "Landskip Yard Care",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "72 Bishopgate Street",
+ "address_type": "Office",
+ "city": "SEAHAM",
+ "country": "United Kingdom",
+ "customer": "Life Plan Counselling",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "\u03a3\u03ba\u03b1\u03c6\u03af\u03b4\u03b9\u03b1 105",
+ "address_type": "Office",
+ "city": "\u03a0\u0391\u03a1\u0395\u039a\u039a\u039b\u0397\u03a3\u0399\u0391",
+ "country": "Cyprus",
+ "customer": "Mr Fables",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "Mellemvej 7",
+ "address_type": "Office",
+ "city": "Aabybro",
+ "country": "Denmark",
+ "customer": "Nelson Brothers",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "Plougg\u00e5rdsvej 98",
+ "address_type": "Office",
+ "city": "Karby",
+ "country": "Denmark",
+ "customer": "Netobill",
+ "phone": "23566775757",
+ "supplier": null
+ },
+ {
+ "address_line1": "176 Michalakopoulou Street",
+ "address_type": "Office",
+ "city": "Agio Georgoudi",
+ "country": "Cyprus",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "Helios Air"
+ },
+ {
+ "address_line1": "Fibichova 1102",
+ "address_type": "Office",
+ "city": "Kokor\u00edn",
+ "country": "Czech Republic",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "Ks Merchandise"
+ },
+ {
+ "address_line1": "Zahradn\u00ed 888",
+ "address_type": "Office",
+ "city": "Cecht\u00edn",
+ "country": "Czech Republic",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "HomeBase"
+ },
+ {
+ "address_line1": "ul. Grochowska 94",
+ "address_type": "Office",
+ "city": "Warszawa",
+ "country": "Poland",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "Scott Ties"
+ },
+ {
+ "address_line1": "Norra Esplanaden 87",
+ "address_type": "Office",
+ "city": "HELSINKI",
+ "country": "Finland",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "Reliable Investments"
+ },
+ {
+ "address_line1": "2038 Fallon Drive",
+ "address_type": "Office",
+ "city": "Dresden",
+ "country": "Canada",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "Nan Duskin"
+ },
+ {
+ "address_line1": "77 cours Franklin Roosevelt",
+ "address_type": "Office",
+ "city": "MARSEILLE",
+ "country": "France",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "Rainbow Records"
+ },
+ {
+ "address_line1": "ul. Tuwima Juliana 85",
+ "address_type": "Office",
+ "city": "\u0141\u00f3d\u017a",
+ "country": "Poland",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "New World Realty"
+ },
+ {
+ "address_line1": "Gl. Sygehusvej 41",
+ "address_type": "Office",
+ "city": "Narsaq",
+ "country": "Greenland",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "Asiatic Solutions"
+ },
+ {
+ "address_line1": "Gosposka ulica 50",
+ "address_type": "Office",
+ "city": "Nova Gorica",
+ "country": "Slovenia",
+ "customer": null,
+ "phone": "23566775757",
+ "supplier": "Eagle Hardware"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/data/bom.json b/erpnext/demo/data/bom.json
new file mode 100644
index 0000000..145c0ba
--- /dev/null
+++ b/erpnext/demo/data/bom.json
@@ -0,0 +1,147 @@
+[
+ {
+ "item": "Bearing Assembly",
+ "items": [
+ {
+ "item_code": "Base Bearing Plate",
+ "qty": 1.0,
+ "rate": 15.0
+ },
+ {
+ "item_code": "Bearing Block",
+ "qty": 1.0,
+ "rate": 10.0
+ },
+ {
+ "item_code": "Bearing Collar",
+ "qty": 2.0,
+ "rate": 20.0
+ },
+ {
+ "item_code": "Bearing Pipe",
+ "qty": 1.0,
+ "rate": 15.0
+ },
+ {
+ "item_code": "Upper Bearing Plate",
+ "qty": 1.0,
+ "rate": 50.0
+ }
+ ]
+ },
+ {
+ "item": "Wind Mill A Series",
+ "items": [
+ {
+ "item_code": "Base Bearing Plate",
+ "qty": 1.0,
+ "rate": 15.0
+ },
+ {
+ "item_code": "Base Plate",
+ "qty": 1.0,
+ "rate": 20.0
+ },
+ {
+ "item_code": "Bearing Block",
+ "qty": 1.0,
+ "rate": 10.0
+ },
+ {
+ "item_code": "Bearing Pipe",
+ "qty": 1.0,
+ "rate": 15.0
+ },
+ {
+ "item_code": "External Disc",
+ "qty": 1.0,
+ "rate": 45.0
+ },
+ {
+ "item_code": "Shaft",
+ "qty": 1.0,
+ "rate": 30.0
+ },
+ {
+ "item_code": "Wing Sheet",
+ "qty": 4.0,
+ "rate": 22.0
+ }
+ ]
+ },
+ {
+ "item": "Wind MIll C Series",
+ "items": [
+ {
+ "item_code": "Base Plate",
+ "qty": 2.0,
+ "rate": 20.0
+ },
+ {
+ "item_code": "Internal Disc",
+ "qty": 1.0,
+ "rate": 33.0
+ },
+ {
+ "item_code": "External Disc",
+ "qty": 1.0,
+ "rate": 45.0
+ },
+ {
+ "item_code": "Bearing Assembly",
+ "qty": 1.0,
+ "rate": 130.0
+ },
+ {
+ "item_code": "Wing Sheet",
+ "qty": 3.0,
+ "rate": 22.0
+ }
+ ]
+ },
+ {
+ "item": "Wind Turbine",
+ "items": [
+ {
+ "item_code": "Base Bearing Plate",
+ "qty": 1.0,
+ "rate": 15.0
+ },
+ {
+ "item_code": "Base Plate",
+ "qty": 1.0,
+ "rate": 20.0
+ },
+ {
+ "item_code": "Bearing Collar",
+ "qty": 1.0,
+ "rate": 20.0
+ },
+ {
+ "item_code": "Blade Rib",
+ "qty": 1.0,
+ "rate": 10.0
+ },
+ {
+ "item_code": "Shaft",
+ "qty": 1.0,
+ "rate": 30.0
+ },
+ {
+ "item_code": "Wing Sheet",
+ "qty": 2.0,
+ "rate": 22.0
+ }
+ ]
+ },
+ {
+ "item": "Base Plate",
+ "items": [
+ {
+ "item_code": "Base Plate Un Painted",
+ "qty": 1.0,
+ "rate": 16.0
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/data/contact.json b/erpnext/demo/data/contact.json
new file mode 100644
index 0000000..130845b
--- /dev/null
+++ b/erpnext/demo/data/contact.json
@@ -0,0 +1,191 @@
+[
+ {
+ "customer": "Adaptas",
+ "email_id": "JanVaclavik@example.com",
+ "first_name": "January",
+ "last_name": "V\u00e1clav\u00edk",
+ "supplier": null
+ },
+ {
+ "customer": "Asian Fusion",
+ "email_id": "ChidumagaTobeolisa@example.com",
+ "first_name": "Chidumaga",
+ "last_name": "Tobeolisa",
+ "supplier": null
+ },
+ {
+ "customer": "Asian Junction",
+ "email_id": "JanaKubanova@example.com",
+ "first_name": "Jana",
+ "last_name": "Kub\u00e1\u0148ov\u00e1",
+ "supplier": null
+ },
+ {
+ "customer": "Big D Supermarkets",
+ "email_id": "XuChaoXuan@example.com",
+ "first_name": "\u7d39\u8431",
+ "last_name": "\u4e8e",
+ "supplier": null
+ },
+ {
+ "customer": "Buttrey Food & Drug",
+ "email_id": "OzlemVerwijmeren@example.com",
+ "first_name": "\u00d6zlem",
+ "last_name": "Verwijmeren",
+ "supplier": null
+ },
+ {
+ "customer": "Chi-Chis",
+ "email_id": "HansRasmussen@example.com",
+ "first_name": "Hans",
+ "last_name": "Rasmussen",
+ "supplier": null
+ },
+ {
+ "customer": "Choices",
+ "email_id": "SatomiShigeki@example.com",
+ "first_name": "Satomi",
+ "last_name": "Shigeki",
+ "supplier": null
+ },
+ {
+ "customer": "Consumers and Consumers Express",
+ "email_id": "SimonVJessen@example.com",
+ "first_name": "Simon",
+ "last_name": "Jessen",
+ "supplier": null
+ },
+ {
+ "customer": "Crafts Canada",
+ "email_id": "NeguaranShahsaah@example.com",
+ "first_name": "\u0646\u06af\u0627\u0631\u06cc\u0646",
+ "last_name": "\u0634\u0627\u0647 \u0633\u06cc\u0627\u0647",
+ "supplier": null
+ },
+ {
+ "customer": "Endicott Shoes",
+ "email_id": "Lom-AliBataev@example.com",
+ "first_name": "Lom-Ali",
+ "last_name": "Bataev",
+ "supplier": null
+ },
+ {
+ "customer": "Fayva",
+ "email_id": "VanNgocTien@example.com",
+ "first_name": "Ti\u00ean",
+ "last_name": "V\u0103n",
+ "supplier": null
+ },
+ {
+ "customer": "Intelacard",
+ "email_id": "QuimeyOsorioRuelas@example.com",
+ "first_name": "Quimey",
+ "last_name": "Osorio",
+ "supplier": null
+ },
+ {
+ "customer": "Landskip Yard Care",
+ "email_id": "EdgardaSalcedoRaya@example.com",
+ "first_name": "Edgarda",
+ "last_name": "Salcedo",
+ "supplier": null
+ },
+ {
+ "customer": "Life Plan Counselling",
+ "email_id": "HafsteinnBjarnarsonar@example.com",
+ "first_name": "Hafsteinn",
+ "last_name": "Bjarnarsonar",
+ "supplier": null
+ },
+ {
+ "customer": "Mr Fables",
+ "email_id": "\u0434\u0430\u043d\u0438\u0438\u043b@example.com",
+ "first_name": "\u0414\u0430\u043d\u0438\u0438\u043b",
+ "last_name": "\u041a\u043e\u043d\u043e\u0432\u0430\u043b\u043e\u0432",
+ "supplier": null
+ },
+ {
+ "customer": "Nelson Brothers",
+ "email_id": "SelmaMAndersen@example.com",
+ "first_name": "Selma",
+ "last_name": "Andersen",
+ "supplier": null
+ },
+ {
+ "customer": "Netobill",
+ "email_id": "LadislavKolaja@example.com",
+ "first_name": "Ladislav",
+ "last_name": "Kolaja",
+ "supplier": null
+ },
+ {
+ "customer": null,
+ "email_id": "TewoldeAbaalom@example.com",
+ "first_name": "Tewolde",
+ "last_name": "Abaalom",
+ "supplier": "Helios Air"
+ },
+ {
+ "customer": null,
+ "email_id": "LeilaFernandesRodrigues@example.com",
+ "first_name": "Leila",
+ "last_name": "Rodrigues",
+ "supplier": "Ks Merchandise"
+ },
+ {
+ "customer": null,
+ "email_id": "DmitryBulgakov@example.com",
+ "first_name": "Dmitry",
+ "last_name": "Bulgakov",
+ "supplier": "HomeBase"
+ },
+ {
+ "customer": null,
+ "email_id": "HaiducWhitfoot@example.com",
+ "first_name": "Haiduc",
+ "last_name": "Whitfoot",
+ "supplier": "Scott Ties"
+ },
+ {
+ "customer": null,
+ "email_id": "SesseljaPetursdottir@example.com",
+ "first_name": "Sesselja",
+ "last_name": "P\u00e9tursd\u00f3ttir",
+ "supplier": "Reliable Investments"
+ },
+ {
+ "customer": null,
+ "email_id": "HajdarPignar@example.com",
+ "first_name": "Hajdar",
+ "last_name": "Pignar",
+ "supplier": "Nan Duskin"
+ },
+ {
+ "customer": null,
+ "email_id": "GustavaLorenzo@example.com",
+ "first_name": "Gustava",
+ "last_name": "Lorenzo",
+ "supplier": "Rainbow Records"
+ },
+ {
+ "customer": null,
+ "email_id": "BethanyWood@example.com",
+ "first_name": "Bethany",
+ "last_name": "Wood",
+ "supplier": "New World Realty"
+ },
+ {
+ "customer": null,
+ "email_id": "GlorianaBrownlock@example.com",
+ "first_name": "Gloriana",
+ "last_name": "Brownlock",
+ "supplier": "Asiatic Solutions"
+ },
+ {
+ "customer": null,
+ "email_id": "JensonFraser@gustr.com",
+ "first_name": "Jenson",
+ "last_name": "Fraser",
+ "supplier": "Eagle Hardware"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/data/employee.json b/erpnext/demo/data/employee.json
new file mode 100644
index 0000000..d9f4d2a
--- /dev/null
+++ b/erpnext/demo/data/employee.json
@@ -0,0 +1,72 @@
+[
+ {
+ "date_of_birth": "1982-01-03",
+ "date_of_joining": "2001-10-10",
+ "employee_name": "Dikman Shervashidze Shervashidze",
+ "gender": "Female",
+ "user_id": "DikmanShervashidze@example.com"
+ },
+ {
+ "date_of_birth": "1959-02-03",
+ "date_of_joining": "1976-09-16",
+ "employee_name": "Zukutakitoteka",
+ "gender": "Female",
+ "user_id": "Zukutakitoteka@example.com"
+ },
+ {
+ "date_of_birth": "1982-03-03",
+ "date_of_joining": "2000-06-16",
+ "employee_name": "Hatsue Kashiwagi",
+ "gender": "Female",
+ "user_id": "HatsueKashiwagi@example.com"
+ },
+ {
+ "date_of_birth": "1945-04-04",
+ "date_of_joining": "1969-07-01",
+ "employee_name": "Nuran Verkleij",
+ "gender": "Female",
+ "user_id": "NuranVerkleij@example.com"
+ },
+ {
+ "date_of_birth": "1978-05-03",
+ "date_of_joining": "1999-12-24",
+ "employee_name": "\u0414\u043c\u0438\u0442\u0440\u0438\u0439 \u041f\u0438\u0440\u043e\u0433\u043e\u0432",
+ "gender": "Male",
+ "user_id": "aromn@example.com"
+ },
+ {
+ "date_of_birth": "1964-06-03",
+ "date_of_joining": "1981-08-05",
+ "employee_name": "Tilde Lindqvist",
+ "gender": "Female",
+ "user_id": "TildeLindqvist@example.com"
+ },
+ {
+ "date_of_birth": "1982-07-03",
+ "date_of_joining": "2006-06-10",
+ "employee_name": "Micha\u0142 Sobczak",
+ "gender": "Male",
+ "user_id": "MichalSobczak@example.com"
+ },
+ {
+ "date_of_birth": "1969-08-03",
+ "date_of_joining": "1993-10-21",
+ "employee_name": "Gabrielle Loftus",
+ "gender": "Female",
+ "user_id": "GabrielleLoftus@example.com"
+ },
+ {
+ "date_of_birth": "1982-09-03",
+ "date_of_joining": "2005-09-06",
+ "employee_name": "Vakhita Ryzaev",
+ "gender": "Male",
+ "user_id": "VakhitaRyzaev@example.com"
+ },
+ {
+ "date_of_birth": "1985-10-03",
+ "date_of_joining": "2007-12-25",
+ "employee_name": "Charmaine Gaudreau",
+ "gender": "Female",
+ "user_id": "CharmaineGaudreau@example.com"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/data/item.json b/erpnext/demo/data/item.json
new file mode 100644
index 0000000..30436d1
--- /dev/null
+++ b/erpnext/demo/data/item.json
@@ -0,0 +1,224 @@
+[
+ {
+ "default_supplier": "Asiatic Solutions",
+ "default_warehouse": "Stores",
+ "description": "For Upper Bearing",
+ "image": "/assets/erpnext_demo/images/disc.png",
+ "item_code": "Disc Collars",
+ "item_group": "Raw Material",
+ "item_name": "Disc Collars"
+ },
+ {
+ "default_supplier": "Nan Duskin",
+ "default_warehouse": "Stores",
+ "description": "CAST IRON, MCMASTER PART NO. 3710T13",
+ "image": "/assets/erpnext_demo/images/bearing.jpg",
+ "item_code": "Bearing Block",
+ "item_group": "Raw Material",
+ "item_name": "Bearing Block"
+ },
+ {
+ "default_supplier": null,
+ "default_warehouse": "Finished Goods",
+ "description": "Wind Mill C Series for Commercial Use 18ft",
+ "image": "/assets/erpnext_demo/images/wind-turbine-2.png",
+ "item_code": "Wind MIll C Series",
+ "item_group": "Products",
+ "item_name": "Wind MIll C Series"
+ },
+ {
+ "default_supplier": null,
+ "default_warehouse": "Finished Goods",
+ "description": "Wind Mill A Series for Home Use 9ft",
+ "image": "/assets/erpnext_demo/images/wind-turbine.png",
+ "item_code": "Wind Mill A Series",
+ "item_group": "Products",
+ "item_name": "Wind Mill A Series"
+ },
+ {
+ "default_supplier": null,
+ "default_warehouse": "Finished Goods",
+ "description": "Small Wind Turbine for Home Use\n\n\n<!-- html -->",
+ "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg",
+ "item_code": "Wind Turbine",
+ "item_group": "Products",
+ "item_name": "Wind Turbine",
+ "has_variants": 1,
+ "has_serial_no": 1,
+ "attributes":[
+ { "attribute": "Size" }
+ ]
+ },
+ {
+ "default_supplier": "HomeBase",
+ "default_warehouse": "Stores",
+ "description": "1.5 in. Diameter x 36 in. Mild Steel Tubing",
+ "image": null,
+ "item_code": "Bearing Pipe",
+ "item_group": "Raw Material",
+ "item_name": "Bearing Pipe"
+ },
+ {
+ "default_supplier": "New World Realty",
+ "default_warehouse": "Stores",
+ "description": "1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet",
+ "image": null,
+ "item_code": "Wing Sheet",
+ "item_group": "Raw Material",
+ "item_name": "Wing Sheet"
+ },
+ {
+ "default_supplier": "Eagle Hardware",
+ "default_warehouse": "Stores",
+ "description": "3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate",
+ "image": null,
+ "item_code": "Upper Bearing Plate",
+ "item_group": "Raw Material",
+ "item_name": "Upper Bearing Plate"
+ },
+ {
+ "default_supplier": "Asiatic Solutions",
+ "default_warehouse": "Stores",
+ "description": "Bearing Assembly",
+ "image": null,
+ "item_code": "Bearing Assembly",
+ "item_group": "Sub Assemblies",
+ "item_name": "Bearing Assembly"
+ },
+ {
+ "default_supplier": "HomeBase",
+ "default_warehouse": "Stores",
+ "description": "3/4 in. x 2 ft. x 4 ft. Pine Plywood",
+ "image": null,
+ "item_code": "Base Plate",
+ "item_group": "Raw Material",
+ "item_name": "Base Plate"
+ },
+ {
+ "default_supplier": "Scott Ties",
+ "default_warehouse": "Stores",
+ "description": "N/A",
+ "image": null,
+ "item_code": "Stand",
+ "item_group": "Raw Material",
+ "item_name": "Stand"
+ },
+ {
+ "default_supplier": "Eagle Hardware",
+ "default_warehouse": "Stores",
+ "description": "1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar",
+ "image": null,
+ "item_code": "Bearing Collar",
+ "item_group": "Raw Material",
+ "item_name": "Bearing Collar"
+ },
+ {
+ "default_supplier": "Eagle Hardware",
+ "default_warehouse": "Stores",
+ "description": "1/4 in. x 6 in. x 6 in. Mild Steel Plate",
+ "image": null,
+ "item_code": "Base Bearing Plate",
+ "item_group": "Raw Material",
+ "item_name": "Base Bearing Plate"
+ },
+ {
+ "default_supplier": "HomeBase",
+ "default_warehouse": "Stores",
+ "description": "15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing",
+ "image": null,
+ "item_code": "External Disc",
+ "item_group": "Raw Material",
+ "item_name": "External Disc"
+ },
+ {
+ "default_supplier": "Eagle Hardware",
+ "default_warehouse": "Stores",
+ "description": "1.25 in. Diameter x 6 ft. Mild Steel Tubing",
+ "image": null,
+ "item_code": "Shaft",
+ "item_group": "Raw Material",
+ "item_name": "Shaft"
+ },
+ {
+ "default_supplier": "Ks Merchandise",
+ "default_warehouse": "Stores",
+ "description": "1/2 in. x 2 ft. x 4 ft. Pine Plywood",
+ "image": null,
+ "item_code": "Blade Rib",
+ "item_group": "Raw Material",
+ "item_name": "Blade Rib"
+ },
+ {
+ "default_supplier": "HomeBase",
+ "default_warehouse": "Stores",
+ "description": "For Bearing Collar",
+ "image": null,
+ "item_code": "Internal Disc",
+ "item_group": "Raw Material",
+ "item_name": "Internal Disc"
+ },
+ {
+ "default_supplier": null,
+ "default_warehouse": "Finished Goods",
+ "description": "Small Wind Turbine for Home Use\n\n\n<!-- html -->\n<p>Size: Small</p>",
+ "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg",
+ "item_code": "Wind Turbine-S",
+ "item_group": "Products",
+ "item_name": "Wind Turbine-S",
+ "variant_of": "Wind Turbine",
+ "attributes":[
+ {
+ "attribute": "Size",
+ "attribute_value": "Small"
+ }
+ ]
+ },
+ {
+ "default_supplier": null,
+ "default_warehouse": "Finished Goods",
+ "description": "Small Wind Turbine for Home Use\n\n\n<!-- html -->\n<p>Size: Medium</p>",
+ "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg",
+ "item_code": "Wind Turbine-M",
+ "item_group": "Products",
+ "item_name": "Wind Turbine-M",
+ "variant_of": "Wind Turbine",
+ "attributes":[
+ {
+ "attribute": "Size",
+ "attribute_value": "Medium"
+ }
+ ]
+ },
+ {
+ "default_supplier": null,
+ "default_warehouse": "Finished Goods",
+ "description": "Small Wind Turbine for Home Use\n\n\n<!-- html -->\n<p>Size: Large</p>",
+ "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg",
+ "item_code": "Wind Turbine-L",
+ "item_group": "Products",
+ "item_name": "Wind Turbine-L",
+ "variant_of": "Wind Turbine",
+ "attributes":[
+ {
+ "attribute": "Size",
+ "attribute_value": "Large"
+ }
+ ]
+ },
+ {
+ "is_stock_item": 0,
+ "description": "Wind Mill A Series with Spare Bearing",
+ "item_code": "Wind Mill A Series with Spare Bearing",
+ "item_group": "Products",
+ "item_name": "Wind Mill A Series with Spare Bearing"
+ },
+ {
+ "default_supplier": "HomeBase",
+ "default_warehouse": "Stores",
+ "description": "3/4 in. x 2 ft. x 4 ft. Pine Plywood",
+ "image": null,
+ "item_code": "Base Plate Un Painted",
+ "item_group": "Raw Material",
+ "item_name": "Base Plate Un Painted"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/data/lead.json b/erpnext/demo/data/lead.json
new file mode 100644
index 0000000..ff78877
--- /dev/null
+++ b/erpnext/demo/data/lead.json
@@ -0,0 +1,127 @@
+[
+ {
+ "company_name": "Zany Brainy",
+ "email_id": "MartLakeman@example.com",
+ "lead_name": "Mart Lakeman"
+ },
+ {
+ "company_name": "Patterson-Fletcher",
+ "email_id": "SagaLundqvist@example.com",
+ "lead_name": "Saga Lundqvist"
+ },
+ {
+ "company_name": "Griff's Hamburgers",
+ "email_id": "AdnaSjoberg@example.com",
+ "lead_name": "Adna Sj\u00f6berg"
+ },
+ {
+ "company_name": "Rhodes Furniture",
+ "email_id": "IdaDSvendsen@example.com",
+ "lead_name": "Ida Svendsen"
+ },
+ {
+ "company_name": "Burger Chef",
+ "email_id": "EmppuHameenniemi@example.com",
+ "lead_name": "Emppu H\u00e4meenniemi"
+ },
+ {
+ "company_name": "Stratabiz",
+ "email_id": "EugenioPisano@example.com",
+ "lead_name": "Eugenio Pisano"
+ },
+ {
+ "company_name": "Home Quarters Warehouse",
+ "email_id": "SemharHagos@example.com",
+ "lead_name": "Semhar Hagos"
+ },
+ {
+ "company_name": "Enviro Architectural Designs",
+ "email_id": "BranimiraIvankovic@example.com",
+ "lead_name": "Branimira Ivankovi\u0107"
+ },
+ {
+ "company_name": "Ideal Garden Management",
+ "email_id": "ShellyLFields@example.com",
+ "lead_name": "Shelly Fields"
+ },
+ {
+ "company_name": "Listen Up",
+ "email_id": "LeoMikulic@example.com",
+ "lead_name": "Leo Mikuli\u0107"
+ },
+ {
+ "company_name": "I. Magnin",
+ "email_id": "DenisaJarosova@example.com",
+ "lead_name": "Denisa Jaro\u0161ov\u00e1"
+ },
+ {
+ "company_name": "First Rate Choice",
+ "email_id": "JanekRutkowski@example.com",
+ "lead_name": "Janek Rutkowski"
+ },
+ {
+ "company_name": "Multi Tech Development",
+ "email_id": "mm@example.com",
+ "lead_name": "\u7f8e\u6708 \u5b87\u85e4"
+ },
+ {
+ "company_name": "National Auto Parts",
+ "email_id": "dd@example.com",
+ "lead_name": "\u0414\u0430\u043d\u0438\u0438\u043b \u0410\u0444\u0430\u043d\u0430\u0441\u044c\u0435\u0432"
+ },
+ {
+ "company_name": "Integra Investment Plan",
+ "email_id": "ZorislavPetkovic@example.com",
+ "lead_name": "Zorislav Petkovi\u0107"
+ },
+ {
+ "company_name": "The Lawn Guru",
+ "email_id": "NanaoNiwa@example.com",
+ "lead_name": "Nanao Niwa"
+ },
+ {
+ "company_name": "Buena Vista Realty Service",
+ "email_id": "HreiarJorundsson@example.com",
+ "lead_name": "Hrei\u00f0ar J\u00f6rundsson"
+ },
+ {
+ "company_name": "Bountiful Harvest Health Food Store",
+ "email_id": "ChuThiBichLai@example.com",
+ "lead_name": "Lai Chu"
+ },
+ {
+ "company_name": "P. Samuels Men's Clothiers",
+ "email_id": "VictorAksakov@example.com",
+ "lead_name": "Victor Aksakov"
+ },
+ {
+ "company_name": "Vinyl Fever",
+ "email_id": "SaidalimBisliev@example.com",
+ "lead_name": "Saidalim Bisliev"
+ },
+ {
+ "company_name": "Garden Master",
+ "email_id": "TotteJakobsson@example.com",
+ "lead_name": "Totte Jakobsson"
+ },
+ {
+ "company_name": "Big Apple",
+ "email_id": "NanaArmasRobles@example.com",
+ "lead_name": "Nan\u00e1 Armas"
+ },
+ {
+ "company_name": "Monk House Sales",
+ "email_id": "WalerianDuda@example.com",
+ "lead_name": "Walerian Duda"
+ },
+ {
+ "company_name": "ManCharm",
+ "email_id": "Moarimikashi@example.com",
+ "lead_name": "Moarimikashi"
+ },
+ {
+ "company_name": "Custom Lawn Care",
+ "email_id": "DobromilDabrowski@example.com",
+ "lead_name": "Dobromi\u0142 D\u0105browski"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/data/operation.json b/erpnext/demo/data/operation.json
new file mode 100644
index 0000000..47f26d1
--- /dev/null
+++ b/erpnext/demo/data/operation.json
@@ -0,0 +1,32 @@
+[
+ {
+ "description": "Setup Fixtures for Assembly",
+ "name": "Setup Fixtures",
+ "workstation": "Assembly Station 1"
+ },
+ {
+ "description": "Assemble Unit as per Standard Operating Procedures",
+ "name": "Assembly Operation",
+ "workstation": "Assembly Station 1"
+ },
+ {
+ "description": "Final Testing Checklist",
+ "name": "Testing",
+ "workstation": "Packing and Testing Station"
+ },
+ {
+ "description": "Final Packing and add Instructions",
+ "name": "Packing",
+ "workstation": "Packing and Testing Station"
+ },
+ {
+ "description": "Prepare frame for assembly",
+ "name": "Prepare Frame",
+ "workstation": "Drilling Machine 1"
+ },
+ {
+ "description": "Connect wires",
+ "name": "Wiring",
+ "workstation": "Assembly Station 1"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/data/user.json b/erpnext/demo/data/user.json
new file mode 100644
index 0000000..e353efb
--- /dev/null
+++ b/erpnext/demo/data/user.json
@@ -0,0 +1,112 @@
+[
+ {
+ "email": "test_demo@erpnext.com",
+ "first_name": "Test",
+ "last_name": "User"
+ },
+ {
+ "email": "DikmanShervashidze@example.com",
+ "first_name": "Dikman",
+ "last_name": "Shervashidze"
+ },
+ {
+ "email": "Zukutakitoteka@example.com",
+ "first_name": "Zukutakitoteka",
+ "last_name": null
+ },
+ {
+ "email": "HatsueKashiwagi@example.com",
+ "first_name": "Hatsue",
+ "last_name": "Kashiwagi"
+ },
+ {
+ "email": "NuranVerkleij@example.com",
+ "first_name": "Nuran",
+ "last_name": "Verkleij"
+ },
+ {
+ "email": "aromn@example.com",
+ "first_name": "\u0414\u043c\u0438\u0442\u0440\u0438\u0439",
+ "last_name": "\u041f\u0438\u0440\u043e\u0433\u043e\u0432"
+ },
+ {
+ "email": "TildeLindqvist@example.com",
+ "first_name": "Tilde",
+ "last_name": "Lindqvist"
+ },
+ {
+ "email": "MichalSobczak@example.com",
+ "first_name": "Micha\u0142",
+ "last_name": "Sobczak"
+ },
+ {
+ "email": "GabrielleLoftus@example.com",
+ "first_name": "Gabrielle",
+ "last_name": "Loftus"
+ },
+ {
+ "email": "VakhitaRyzaev@example.com",
+ "first_name": "Vakhita",
+ "last_name": "Ryzaev"
+ },
+ {
+ "email": "CharmaineGaudreau@example.com",
+ "first_name": "Charmaine",
+ "last_name": "Gaudreau"
+ },
+ {
+ "email": "RafaelaMaartens@example.com",
+ "first_name": "Rafa\u00ebla",
+ "last_name": "Maartens"
+ },
+ {
+ "email": "NuguseYohannes@example.com",
+ "first_name": "Nuguse",
+ "last_name": "Yohannes"
+ },
+ {
+ "email": "panca@example.com",
+ "first_name": "\u0420\u0430\u0438\u0441\u0430",
+ "last_name": "\u0411\u0435\u043b\u044f\u043a\u043e\u0432\u0430"
+ },
+ {
+ "email": "CaYinLong@example.com",
+ "first_name": "\u80e4\u9686",
+ "last_name": "\u8521"
+ },
+ {
+ "email": "FreddieScott@example.com",
+ "first_name": "Freddie",
+ "last_name": "Scott"
+ },
+ {
+ "email": "BergoraVigfusdottir@example.com",
+ "first_name": "Berg\u00fe\u00f3ra",
+ "last_name": "Vigf\u00fasd\u00f3ttir"
+ },
+ {
+ "email": "WardNajmalDinKalb@example.com",
+ "first_name": "Ward",
+ "last_name": "Kalb"
+ },
+ {
+ "email": "WanMai@example.com",
+ "first_name": "Wan",
+ "last_name": "Mai"
+ },
+ {
+ "email": "LeonAbdulov@example.com",
+ "first_name": "Leon",
+ "last_name": "Abdulov"
+ },
+ {
+ "email": "SabinaNovotna@example.com",
+ "first_name": "Sabina",
+ "last_name": "Novotn\u00e1"
+ },
+ {
+ "email": "demo@erpnext.com",
+ "first_name": "Demo",
+ "last_name": "User"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/demo/demo.py b/erpnext/demo/demo.py
new file mode 100644
index 0000000..3ca8eb9
--- /dev/null
+++ b/erpnext/demo/demo.py
@@ -0,0 +1,54 @@
+from __future__ import unicode_literals
+
+import frappe, sys
+import erpnext
+import frappe.utils
+from erpnext.demo.setup_data import setup_data
+from erpnext.demo.user import hr, sales
+
+def make(domain='Manufacturing'):
+ frappe.flags.domain = domain
+ setup_data()
+ simulate()
+
+def simulate():
+ runs_for = frappe.flags.runs_for or 150
+ frappe.flags.company = erpnext.get_default_company()
+
+ if not frappe.flags.start_date:
+ # start date = 100 days back
+ frappe.flags.start_date = frappe.utils.add_days(frappe.utils.nowdate(), -1 * runs_for)
+
+ current_date = frappe.utils.getdate(frappe.flags.start_date)
+
+ # continue?
+ demo_last_date = frappe.db.get_global('demo_last_date')
+ if demo_last_date:
+ current_date = frappe.utils.add_days(demo_last_date, 1)
+
+ # run till today
+ if not runs_for:
+ runs_for = frappe.utils.date_diff(frappe.utils.nowdate(), current_date)
+ # runs_for = 100
+
+ for i in xrange(runs_for):
+ sys.stdout.write("\rSimulating {0}".format(current_date.strftime("%Y-%m-%d")))
+ sys.stdout.flush()
+ frappe.flags.current_date = current_date
+
+ if current_date.weekday() in (5, 6):
+ current_date = frappe.utils.add_days(current_date, 1)
+ continue
+
+ hr.work()
+ sales.work()
+ # run_purchase()
+ # run_manufacturing()
+ # run_stock()
+ # run_accounts()
+ # run_projects()
+ # run_messages()
+
+ current_date = frappe.utils.add_days(current_date, 1)
+
+ frappe.db.commit()
diff --git a/erpnext/demo/domains.py b/erpnext/demo/domains.py
new file mode 100644
index 0000000..0217f24
--- /dev/null
+++ b/erpnext/demo/domains.py
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+
+data = {
+ 'Manufacturing': {
+ 'company_name': 'Wind Power LLC'
+ },
+ 'Retail': {
+ 'company_name': 'Annapurna Dairy Shop',
+ },
+ 'Distribution': {
+ 'company_name': 'Soltice Hardware',
+ },
+ 'Services': {
+ 'company_name': 'Acme Consulting'
+ }
+}
\ No newline at end of file
diff --git a/erpnext/demo/setup_data.py b/erpnext/demo/setup_data.py
new file mode 100644
index 0000000..bfaee47
--- /dev/null
+++ b/erpnext/demo/setup_data.py
@@ -0,0 +1,302 @@
+from __future__ import unicode_literals
+
+import random, json
+from erpnext.demo.domains import data
+import frappe, erpnext
+import frappe.utils
+
+def setup_data():
+ domain = frappe.flags.domain
+ complete_setup(domain)
+ setup_demo_page()
+ setup_fiscal_year()
+ setup_holiday_list()
+ setup_customer()
+ setup_supplier()
+ setup_item()
+ import_json('Address')
+ import_json('Contact')
+ setup_workstation()
+ import_json('Operation')
+ import_json('Lead')
+ setup_item_price()
+ show_item_groups_in_website()
+ setup_currency_exchange()
+ import_json('BOM', submit=True)
+ setup_user()
+ setup_employee()
+ setup_salary_structure()
+ setup_user_roles()
+
+def complete_setup(domain='Manufacturing'):
+ print "Complete Setup..."
+ from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
+
+ if not frappe.get_all('Company', limit=1):
+ setup_complete({
+ "first_name": "Test",
+ "last_name": "User",
+ "email": "test_demo@erpnext.com",
+ "company_tagline": 'Awesome Products and Services',
+ "password": "test",
+ "fy_start_date": "2015-01-01",
+ "fy_end_date": "2015-12-31",
+ "bank_account": "National Bank",
+ "industry": domain,
+ "company_name": data.get(domain).get('company_name'),
+ "chart_of_accounts": "Standard",
+ "company_abbr": ''.join([d[0] for d in data.get(domain).get('company_name').split()]).upper(),
+ "currency": 'USD',
+ "timezone": 'America/New_York',
+ "country": 'United States',
+ "language": "english"
+ })
+
+def setup_demo_page():
+ # home page should always be "start"
+ website_settings = frappe.get_doc("Website Settings", "Website Settings")
+ website_settings.home_page = "demo"
+ website_settings.save()
+
+def setup_fiscal_year():
+ fiscal_year = None
+ for year in xrange(2014, frappe.utils.now_datetime().year + 1, 1):
+ try:
+ fiscal_year = frappe.get_doc({
+ "doctype": "Fiscal Year",
+ "year": frappe.utils.cstr(year),
+ "year_start_date": "{0}-01-01".format(year),
+ "year_end_date": "{0}-12-31".format(year)
+ }).insert()
+ except frappe.DuplicateEntryError:
+ pass
+
+ # set the last fiscal year (current year) as default
+ fiscal_year.set_as_default()
+
+def setup_holiday_list():
+ """Setup Holiday List for the current year"""
+ year = frappe.utils.now_datetime().year
+ holiday_list = frappe.get_doc({
+ "doctype": "Holiday List",
+ "holiday_list_name": str(year),
+ "from_date": "{0}-01-01".format(year),
+ "to_date": "{0}-12-31".format(year),
+ })
+ holiday_list.insert()
+ holiday_list.weekly_off = "Saturday"
+ holiday_list.get_weekly_off_dates()
+ holiday_list.weekly_off = "Sunday"
+ holiday_list.get_weekly_off_dates()
+ holiday_list.save()
+
+ frappe.set_value("Company", erpnext.get_default_company(), "default_holiday_list", holiday_list.name)
+
+def setup_customer():
+ customers = [u'Asian Junction', u'Life Plan Counselling', u'Two Pesos', u'Mr Fables', u'Intelacard', u'Big D Supermarkets', u'Adaptas', u'Nelson Brothers', u'Landskip Yard Care', u'Buttrey Food & Drug', u'Fayva', u'Asian Fusion', u'Crafts Canada', u'Consumers and Consumers Express', u'Netobill', u'Choices', u'Chi-Chis', u'Red Food', u'Endicott Shoes', u'Hind Enterprises']
+ for c in customers:
+ frappe.get_doc({
+ "doctype": "Customer",
+ "customer_name": c,
+ "customer_group": "Commercial",
+ "customer_type": random.choice(["Company", "Individual"]),
+ "territory": "Rest Of The World"
+ }).insert()
+
+def setup_supplier():
+ suppliers = [u'Helios Air', u'Ks Merchandise', u'HomeBase', u'Scott Ties', u'Reliable Investments', u'Nan Duskin', u'Rainbow Records', u'New World Realty', u'Asiatic Solutions', u'Eagle Hardware', u'Modern Electricals']
+ for s in suppliers:
+ frappe.get_doc({
+ "doctype": "Supplier",
+ "supplier_name": s,
+ "supplier_type": random.choice(["Services", "Raw Material"]),
+ }).insert()
+
+def setup_workstation():
+ workstations = [u'Drilling Machine 1', u'Lathe 1', u'Assembly Station 1', u'Assembly Station 2', u'Packing and Testing Station']
+ for w in workstations:
+ frappe.get_doc({
+ "doctype": "Workstation",
+ "workstation_name": w,
+ "holiday_list": frappe.get_all("Holiday List")[0].name,
+ "hour_rate_consumable": int(random.random() * 20),
+ "hour_rate_electricity": int(random.random() * 10),
+ "hour_rate_labour": int(random.random() * 40),
+ "hour_rate_rent": int(random.random() * 10),
+ "working_hours": [
+ {
+ "enabled": 1,
+ "start_time": "8:00:00",
+ "end_time": "15:00:00"
+ }
+ ]
+ }).insert()
+
+def show_item_groups_in_website():
+ """set show_in_website=1 for Item Groups"""
+ products = frappe.get_doc("Item Group", "Products")
+ products.show_in_website = 1
+ products.route = 'products'
+ products.save()
+
+def setup_item():
+ items = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'item.json')).read())
+ for i in items:
+ item = frappe.new_doc('Item')
+ item.update(i)
+ item.default_warehouse = frappe.get_all('Warehouse', filters={'warehouse_name': item.default_warehouse}, limit=1)[0].name
+ item.insert()
+
+def setup_currency_exchange():
+ frappe.get_doc({
+ 'doctype': 'Currency Exchange',
+ 'from_currency': 'EUR',
+ 'to_currency': 'USD',
+ 'exchange_rate': 1.13
+ }).insert()
+
+ frappe.get_doc({
+ 'doctype': 'Currency Exchange',
+ 'from_currency': 'CNY',
+ 'to_currency': 'USD',
+ 'exchange_rate': 0.16
+ }).insert()
+
+def setup_product_bundle():
+ frappe.get_doc({
+ 'doctype': 'Product Bundle',
+ 'new_item_code': 'Wind Mill A Series with Spare Bearing',
+ 'items': [
+ {'item_code': 'Wind Mill A Series', 'qty': 1},
+ {'item_code': 'Bearing Collar', 'qty': 1},
+ {'item_code': 'Bearing Assembly', 'qty': 1},
+ ]
+ }).insert()
+
+def setup_user():
+ frappe.db.sql('delete from tabUser where name not in ("Guest", "Administrator")')
+ for u in json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'user.json')).read()):
+ user = frappe.new_doc("User")
+ user.update(u)
+ user.flags.no_welcome_mail
+ user.password = 'demo'
+ user.insert()
+
+def import_json(doctype, submit=False):
+ frappe.flags.in_import = True
+ data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
+ frappe.scrub(doctype) + '.json')).read())
+ for d in data:
+ doc = frappe.new_doc(doctype)
+ doc.update(d)
+ doc.insert()
+ if submit:
+ doc.submit()
+
+def setup_employee():
+ frappe.db.set_value("HR Settings", None, "emp_created_by", "Naming Series")
+ frappe.db.commit()
+
+ import_json('Employee')
+
+def setup_item_price():
+ frappe.db.sql("delete from `tabItem Price`")
+
+ standard_selling = {
+ "Base Bearing Plate": 28,
+ "Base Plate": 21,
+ "Bearing Assembly": 300,
+ "Bearing Block": 14,
+ "Bearing Collar": 103.6,
+ "Bearing Pipe": 63,
+ "Blade Rib": 46.2,
+ "Disc Collars": 42,
+ "External Disc": 56,
+ "Internal Disc": 70,
+ "Shaft": 340,
+ "Stand": 400,
+ "Upper Bearing Plate": 300,
+ "Wind Mill A Series": 320,
+ "Wind Mill A Series with Spare Bearing": 750,
+ "Wind MIll C Series": 400,
+ "Wind Turbine": 400,
+ "Wing Sheet": 30.8
+ }
+
+ standard_buying = {
+ "Base Bearing Plate": 20,
+ "Base Plate": 28,
+ "Base Plate Un Painted": 16,
+ "Bearing Block": 13,
+ "Bearing Collar": 96.4,
+ "Bearing Pipe": 55,
+ "Blade Rib": 38,
+ "Disc Collars": 34,
+ "External Disc": 50,
+ "Internal Disc": 60,
+ "Shaft": 250,
+ "Stand": 300,
+ "Upper Bearing Plate": 200,
+ "Wing Sheet": 25
+ }
+
+ for price_list in ("standard_buying", "standard_selling"):
+ for item, rate in locals().get(price_list).iteritems():
+ frappe.get_doc({
+ "doctype": "Item Price",
+ "price_list": price_list.replace("_", " ").title(),
+ "item_code": item,
+ "selling": 1 if price_list=="standard_selling" else 0,
+ "buying": 1 if price_list=="standard_buying" else 0,
+ "price_list_rate": rate,
+ "currency": "USD"
+ }).insert()
+
+def setup_salary_structure():
+ f = frappe.get_doc('Fiscal Year', frappe.defaults.get_global_default('fiscal_year'))
+
+ for e in frappe.get_all('Employee', fields=['name', 'date_of_joining']):
+ ss = frappe.new_doc('Salary Structure')
+ ss.employee = e.name
+
+ if not e.date_of_joining:
+ continue
+
+ ss.from_date = e.date_of_joining if (e.date_of_joining
+ and e.date_of_joining > f.year_start_date) else f.year_start_date
+ ss.to_date = f.year_end_date
+ ss.append('earnings', {
+ 'earning_type': 'Basic',
+ 'modified_value': random.random() * 10000
+ })
+ ss.append('deductions', {
+ 'deduction_type': 'Income Tax',
+ 'd_modified_amt': random.random() * 1000
+ })
+ ss.insert()
+
+def setup_account():
+ frappe.flags.in_import = True
+ data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
+ 'account.json')).read())
+ for d in data:
+ doc = frappe.new_doc('Account')
+ doc.update(d)
+ doc.parent_account = frappe.db.get_value('Account', {'account_name': doc.parent_account})
+ doc.insert()
+
+def setup_user_roles():
+ if not frappe.db.get_global('demo_hr_user'):
+ user = frappe.get_doc('User', 'CharmaineGaudreau@example.com')
+ user.add_roles('HR User', 'HR Manager', 'Accounts User')
+ frappe.db.set_global('demo_hr_user', user.name)
+
+ if not frappe.db.get_global('demo_sales_user_1'):
+ user = frappe.get_doc('User', 'VakhitaRyzaev@example.com')
+ user.add_roles('Sales User')
+ frappe.db.set_global('demo_sales_user_1', user.name)
+
+ if not frappe.db.get_global('demo_sales_user_2'):
+ user = frappe.get_doc('User', 'GabrielleLoftus@example.com')
+ user.add_roles('Sales User', 'Sales Manager', 'Accounts User')
+ frappe.db.set_global('demo_sales_user_2', user.name)
diff --git a/erpnext/demo/user/__init__.py b/erpnext/demo/user/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/demo/user/__init__.py
diff --git a/erpnext/demo/user/hr.py b/erpnext/demo/user/hr.py
new file mode 100644
index 0000000..8196701
--- /dev/null
+++ b/erpnext/demo/user/hr.py
@@ -0,0 +1,26 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import random_string
+
+def work():
+ frappe.set_user(frappe.db.get_global('demo_hr_user'))
+
+ year, month = frappe.flags.current_date.strftime("%Y-%m").split("-")
+
+ # process payroll
+ if not frappe.db.get_value("Salary Slip", {"month": month, "fiscal_year": year}):
+ process_payroll = frappe.get_doc("Process Payroll", "Process Payroll")
+ process_payroll.company = frappe.flags.company
+ process_payroll.month = month
+ process_payroll.fiscal_year = year
+ process_payroll.create_sal_slip()
+ process_payroll.submit_salary_slip()
+ r = process_payroll.make_journal_entry(frappe.get_value('Account',
+ {'account_name': 'Salary'}))
+
+ journal_entry = frappe.get_doc(r)
+ journal_entry.cheque_no = random_string(10)
+ journal_entry.cheque_date = frappe.flags.current_date
+ journal_entry.posting_date = frappe.flags.current_date
+ journal_entry.insert()
+ journal_entry.submit()
diff --git a/erpnext/demo/user/purchase.py b/erpnext/demo/user/purchase.py
new file mode 100644
index 0000000..cf27358
--- /dev/null
+++ b/erpnext/demo/user/purchase.py
@@ -0,0 +1,138 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe, random
+from frappe.utils.make_random import how_many, can_make, get_random
+from frappe.desk import query_report
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.accounts.party import get_party_account_currency
+from erpnext.exceptions import InvalidCurrency
+from erpnext.stock.doctype.material_request.material_request import make_request_for_quotation
+from erpnext.buying.doctype.request_for_quotation.request_for_quotation import \
+ make_supplier_quotation as make_quotation_from_rfq
+
+def run_purchase(current_date):
+ if random.random() < 0.3:
+ report = "Items To Be Requested"
+ for row in query_report.run(report)["result"][:how_many("Material Request")]:
+ item_code, qty = row[0], abs(row[-1])
+
+ mr = make_material_request(item_code, qty)
+
+ if random.random() < 0.3:
+ for mr in frappe.get_all('Material Request', filters={'material_request_type': 'Purchase', 'status': 'Open'}):
+ rfq = make_request_for_quotation(mr.name)
+ rfq.transaction_date = frappe.flags.current_date
+ add_suppliers(rfq)
+ rfq.save()
+ rfq.submit()
+
+ # Make suppier quotation from RFQ against each supplier.
+ if random.random() < 0.3:
+ for supplier_quotation in frappe.get_all('Request for Quotation', {'status': 'Open'}):
+ rfq = frappe.get_doc('Request for Quotation', rfq.name)
+ for supplier in rfq.suppliers:
+ supplier_quotation = make_quotation_from_rfq(rfq.name, supplier.supplier)
+ supplier_quotation.save()
+ supplier_quotation.submit()
+
+ # get supplier details
+ supplier = get_random("Supplier")
+
+ company_currency = frappe.db.get_value("Company", "Wind Power LLC", "default_currency")
+ party_account_currency = get_party_account_currency("Supplier", supplier, "Wind Power LLC")
+ if company_currency == party_account_currency:
+ exchange_rate = 1
+ else:
+ exchange_rate = get_exchange_rate(party_account_currency, company_currency)
+
+ # make supplier quotations
+ if can_make("Supplier Quotation"):
+ from erpnext.stock.doctype.material_request.material_request import make_supplier_quotation
+
+ report = "Material Requests for which Supplier Quotations are not created"
+ for row in query_report.run(report)["result"][:how_many("Supplier Quotation")]:
+ if row[0] != "'Total'":
+ sq = frappe.get_doc(make_supplier_quotation(row[0]))
+ sq.transaction_date = current_date
+ sq.supplier = supplier
+ sq.currency = party_account_currency or company_currency
+ sq.conversion_rate = exchange_rate
+ sq.insert()
+ sq.submit()
+ frappe.db.commit()
+
+ # make purchase orders
+ if can_make("Purchase Order"):
+ from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+ report = "Requested Items To Be Ordered"
+ for row in query_report.run(report)["result"][:how_many("Purchase Order")]:
+ if row[0] != "'Total'":
+ po = frappe.get_doc(make_purchase_order(row[0]))
+ po.supplier = supplier
+ po.currency = party_account_currency or company_currency
+ po.conversion_rate = exchange_rate
+ po.transaction_date = current_date
+ po.insert()
+ po.submit()
+ frappe.db.commit()
+
+ if can_make("Subcontract"):
+ make_subcontract(current_date)
+
+def make_material_request(item_code, qty):
+ mr = frappe.new_doc("Material Request")
+
+ if frappe.db.get_value('BOM', {'item': item_code, 'is_default': 1, 'is_active': 1}):
+ mr.material_request_type = 'Manufacture'
+ else:
+ mr.material_request_type = "Purchase"
+
+ mr.transaction_date = frappe.flags.current_date
+ mr.append("items", {
+ "doctype": "Material Request Item",
+ "schedule_date": frappe.utils.add_days(mr.transaction_date, 7),
+ "item_code": item_code,
+ "qty": qty
+ })
+ mr.insert()
+ mr.submit()
+ return mr
+
+def add_suppliers(rfq):
+ for i in xrange(2):
+ supplier = get_random("Supplier")
+ if supplier not in [d.supplier for d in rfq.get('suppliers')]:
+ rfq.append("suppliers", { "supplier": supplier })
+
+def make_subcontract():
+ from erpnext.buying.doctype.purchase_order.purchase_order import make_stock_entry
+
+ # make sub-contract PO
+ po = frappe.new_doc("Purchase Order")
+ po.is_subcontracted = "Yes"
+ po.supplier = get_random("Supplier")
+
+ po.append("items", {
+ "item_code": get_random("Item", {"is_sub_contracted_item": 1}),
+ "schedule_date": frappe.utils.add_days(frappe.flags.current_date, 7),
+ "qty": 20
+ })
+ po.set_missing_values()
+ try:
+ po.insert()
+ except InvalidCurrency:
+ return
+
+ po.submit()
+
+ # make material request for
+ make_material_request(po.items[0].item_code, po.items[0].qty)
+
+ # transfer material for sub-contract
+ stock_entry = frappe.get_doc(make_stock_entry(po.name, po.items[0].item_code))
+ stock_entry.from_warehouse = "Stores - WP"
+ stock_entry.to_warehouse = "Supplier - WP"
+ stock_entry.insert()
diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py
new file mode 100644
index 0000000..7432ba0
--- /dev/null
+++ b/erpnext/demo/user/sales.py
@@ -0,0 +1,109 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe, random
+from frappe.utils.make_random import add_random_children, get_random
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.accounts.party import get_party_account_currency
+
+def work():
+ frappe.set_user(frappe.db.get_global('demo_sales_user_2'))
+ if random.random() < 0.5:
+ for i in xrange(random.randint(1,7)):
+ make_opportunity()
+
+ if random.random() < 0.5:
+ for i in xrange(random.randint(1,3)):
+ make_quotation()
+
+ # lost quotations / inquiries
+ if random.random() < 0.3:
+ for i in xrange(random.randint(1,3)):
+ quotation = get_random('Quotation', doc=True)
+ if quotation and quotation.status == 'Submitted':
+ quotation.declare_order_lost('Did not ask')
+
+ for i in xrange(random.randint(1,3)):
+ opportunity = get_random('Opportunity', doc=True)
+ if opportunity and opportunity.status in ('Open', 'Replied'):
+ opportunity.declare_enquiry_lost('Did not ask')
+
+ if random.random() < 0.3:
+ for i in xrange(random.randint(1,3)):
+ make_sales_order()
+
+def make_opportunity():
+ b = frappe.get_doc({
+ "doctype": "Opportunity",
+ "enquiry_from": "Customer",
+ "customer": get_random("Customer"),
+ "enquiry_type": "Sales",
+ "transaction_date": frappe.flags.current_date,
+ })
+
+ add_random_children(b, "items", rows=4, randomize = {
+ "qty": (1, 5),
+ "item_code": ("Item", {"has_variants": "0"})
+ }, unique="item_code")
+
+ b.insert()
+ frappe.db.commit()
+
+def make_quotation():
+ # get open opportunites
+ opportunity = get_random("Opportunity", {"status": "Open"})
+
+ if opportunity:
+ from erpnext.crm.doctype.opportunity.opportunity import make_quotation
+ qtn = frappe.get_doc(make_quotation(opportunity))
+ qtn.insert()
+ frappe.db.commit()
+ qtn.submit()
+ frappe.db.commit()
+ else:
+ # make new directly
+
+ # get customer, currency and exchange_rate
+ customer = get_random("Customer")
+
+ company_currency = frappe.db.get_value("Company", "Wind Power LLC", "default_currency")
+ party_account_currency = get_party_account_currency("Customer", customer, "Wind Power LLC")
+ if company_currency == party_account_currency:
+ exchange_rate = 1
+ else:
+ exchange_rate = get_exchange_rate(party_account_currency, company_currency)
+
+ qtn = frappe.get_doc({
+ "creation": frappe.flags.current_date,
+ "doctype": "Quotation",
+ "quotation_to": "Customer",
+ "customer": customer,
+ "currency": party_account_currency or company_currency,
+ "conversion_rate": exchange_rate,
+ "order_type": "Sales",
+ "transaction_date": frappe.flags.current_date,
+ })
+
+ add_random_children(qtn, "items", rows=3, randomize = {
+ "qty": (1, 5),
+ "item_code": ("Item", {"has_variants": "0"})
+ }, unique="item_code")
+
+ qtn.insert()
+ frappe.db.commit()
+ qtn.submit()
+ frappe.db.commit()
+
+def make_sales_order():
+ q = get_random("Quotation", {"status": "Submitted"})
+ if q:
+ from erpnext.selling.doctype.quotation.quotation import make_sales_order
+ so = frappe.get_doc(make_sales_order(q))
+ so.transaction_date = frappe.flags.current_date
+ so.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10)
+ so.insert()
+ frappe.db.commit()
+ so.submit()
+ frappe.db.commit()
diff --git a/erpnext/docs/user/manual/en/introduction/index.md b/erpnext/docs/user/manual/en/introduction/index.md
index 6471090..f1bcb49 100644
--- a/erpnext/docs/user/manual/en/introduction/index.md
+++ b/erpnext/docs/user/manual/en/introduction/index.md
@@ -14,28 +14,32 @@
Large businesses have the advantage of using advanced data systems to manage
their process efficiently. Small businesses, on the other hand, typically
struggle to keep things organized. They often use a mix of apps like
-spreadsheets, accounting software, web CRM etc to manage.The problem is, not
+spreadsheets, accounting software, web CRM etc to manage. The problem is, not
everyone is on the same page. An ERP changes that.
## What is ERPNext?
-ERPNext helps you to manage all your business information in one application
-and use it to manage operations and take decisions based on data.
+ERPNext is an end-to-end business solution that helps you to manage all your business information in one application
+and use it to not only manage operations but also enables you to take informed decisions well in time to remain ahead of your competition. It forms a backbone of your business to add strength, transparency and control to your enterprise.
Among other things, ERPNext will help you to:
* Track all Invoices and Payments.
* Know what quantity of which product is available in stock.
+ * Identify and track your key performance indicators (KPI's).
* Identify open customer queries.
* Manage payroll.
* Assign tasks and follow up on them.
* Maintain a database of all your customers, suppliers and their contacts.
* Prepare quotes.
+ * Tracking your budgets and spending
+ * Determine effective selling price based on the actual raw material, machinery and effort cost.
* Get reminders on maintenance schedules.
* Publish your website.
And a lot lot lot more.
+
### Topics
{index}
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 15f01ca..d58d3dd 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -33,7 +33,7 @@
on_logout = "erpnext.shopping_cart.utils.clear_cart_count"
remember_selected = ['Company', 'Cost Center', 'Project']
-treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Group', 'Sales Person', 'Territory']
+treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Group', 'Sales Person', 'Territory', "BOM"]
# website
update_website_context = "erpnext.shopping_cart.utils.update_website_context"
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 578e7b7..18b166b 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -18,9 +18,6 @@
class Employee(Document):
- def onload(self):
- self.set_onload('links', self.meta.get_links_setup())
-
def autoname(self):
naming_method = frappe.db.get_value("HR Settings", None, "emp_created_by")
if not naming_method:
@@ -157,22 +154,7 @@
def on_trash(self):
delete_events(self.doctype, self.name)
- def get_timeline_data(self):
- '''returns timeline data based on attendance'''
- return
-
-@frappe.whitelist()
-def get_dashboard_data(name):
- '''load dashboard related data'''
- frappe.has_permission(doc=frappe.get_doc('Employee', name), throw=True)
-
- from frappe.desk.notifications import get_open_count
- return {
- 'count': get_open_count('Employee', name),
- 'timeline_data': get_timeline_data(name),
- }
-
-def get_timeline_data(name):
+def get_timeline_data(doctype, name):
'''Return timeline for attendance'''
return dict(frappe.db.sql('''select unix_timestamp(att_date), count(*)
from `tabAttendance` where employee=%s
@@ -187,7 +169,7 @@
if date_of_birth:
try:
retirement_age = int(frappe.db.get_single_value("HR Settings", "retirement_age") or 60)
- dt = add_years(getdate(date_of_birth),retirement_age)
+ dt = add_years(getdate(date_of_birth),retirement_age)
ret = {'date_of_retirement': dt.strftime('%Y-%m-%d')}
except ValueError:
# invalid date
@@ -256,7 +238,7 @@
holiday_list = frappe.db.get_value("Company", company, "default_holiday_list")
if not holiday_list and raise_exception:
- frappe.throw(_('Please set a default Holiday List for Employee {0} or Company {0}').format(employee, company))
+ frappe.throw(_('Please set a default Holiday List for Employee {0} or Company {1}').format(employee, company))
return holiday_list
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index d6bb12e..bafa008 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -10,7 +10,7 @@
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
"company": cur_frm.doc.company,
- "voucher_type": "Bank Entry"
+ "account_type": "Bank"
},
callback: function(r) {
var jv = frappe.model.make_new_doc_and_get_name('Journal Entry');
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index a15af7d..ae62f1e 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -88,8 +88,8 @@
if not struct:
self.salary_structure = None
- msgprint(_("No active or default Salary Structure found for employee {0} and the month")
- .format(self.employee))
+ frappe.throw(_("No active or default Salary Structure found for employee {0} for the given dates")
+ .format(self.employee), title=_('Salary Structure Missing'))
return struct and struct[0][0] or ''
@@ -261,7 +261,7 @@
self.update_status(self.name)
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
self.email_salary_slip()
-
+
def on_cancel(self):
self.update_status()
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index de8ac94..832402e 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -24,6 +24,9 @@
frm.events.update_cost(frm);
});
frm.add_custom_button(__("Browse BOM"), function() {
+ frappe.route_options = {
+ "bom": frm.doc.name
+ };
frappe.set_route("Tree", "BOM");
});
}
diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js
index 0404360..f6a205f 100644
--- a/erpnext/manufacturing/doctype/bom/bom_tree.js
+++ b/erpnext/manufacturing/doctype/bom/bom_tree.js
@@ -32,4 +32,13 @@
}
}
],
+ menu_items: [
+ {
+ label: __("New BOM"),
+ action: function() {
+ frappe.new_doc("BOM", true)
+ },
+ condition: 'frappe.boot.user.can_create.indexOf("BOM") !== -1'
+ }
+ ]
}
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index db4076e..d4de2e9 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -8,6 +8,7 @@
erpnext.patches.v4_0.move_warehouse_user_to_restrictions
erpnext.patches.v4_0.global_defaults_to_system_settings
erpnext.patches.v4_0.update_incharge_name_to_sales_person_in_maintenance_schedule
+execute:frappe.reload_doc('stock', 'doctype', 'warehouse')
execute:frappe.reload_doc('accounts', 'doctype', 'sales_invoice') # 2014-01-29
execute:frappe.reload_doc('selling', 'doctype', 'sales_order') # 2014-01-29
execute:frappe.reload_doc('selling', 'doctype', 'quotation') # 2014-01-29
@@ -283,3 +284,5 @@
erpnext.patches.v7_0.remove_doctypes_and_reports
erpnext.patches.v7_0.set_is_group_for_warehouse
erpnext.patches.v7_0.update_maintenance_module_in_doctype
+erpnext.patches.v7_0.update_prevdoc_values_for_supplier_quotation_item
+erpnext.patches.v7_0.rename_advance_table_fields
\ No newline at end of file
diff --git a/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py b/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py
index 37b6c63..b1464d5 100644
--- a/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py
+++ b/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py
@@ -5,6 +5,9 @@
import frappe
def execute():
+ frappe.reload_doctype("Sales Invoice Advance")
+ frappe.reload_doctype("Purchase Invoice Advance")
+
je_rows = frappe.db.sql("""
select name, parent, reference_type, reference_name, debit, credit
from `tabJournal Entry Account`
@@ -23,7 +26,7 @@
is_advance_entry=None
if d.reference_type in ("Sales Invoice", "Purchase Invoice") and d.reference_name:
is_advance_entry = frappe.db.sql("""select name from `tab{0}`
- where journal_entry=%s and jv_detail_no=%s
+ where reference_name=%s and reference_row=%s
and ifnull(allocated_amount, 0) > 0 and docstatus=1"""
.format(d.reference_type + " Advance"), (d.parent, d.name))
diff --git a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
index eb727e5..8c2b44a 100644
--- a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
+++ b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
@@ -14,10 +14,12 @@
def get_timesheet_data(data):
return {
+ 'billable': data.billable,
'from_time': data.from_time,
'hours': data.hours,
'to_time': data.to_time,
'project': data.project,
+ 'task': data.task,
'activity_type': data.activity_type,
'operation': data.operation,
'operation_id': data.operation_id,
diff --git a/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py b/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py
index 097097d..252b820 100644
--- a/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py
+++ b/erpnext/patches/v7_0/convert_timelogbatch_to_timesheet.py
@@ -9,22 +9,26 @@
time_sheet.employee= ""
time_sheet.company = frappe.db.get_single_value('Global Defaults', 'default_company')
time_sheet.sales_invoice = tlb.sales_invoice
- if tlb.get('time_logs'):
- for data in tlb.time_logs:
- args = get_timesheet_data(data)
- add_timesheet_detail(time_sheet, args)
- time_sheet.docstatus = tlb.docstatus
- time_sheet.save(ignore_permissions=True)
+ for data in frappe.get_all('Time Log Batch Detail', fields=["*"],
+ filters = {'parent': tlb.name}):
+ args = get_timesheet_data(data)
+ add_timesheet_detail(time_sheet, args)
+
+ time_sheet.docstatus = tlb.docstatus
+ time_sheet.save(ignore_permissions=True)
def get_timesheet_data(data):
- time_log = frappe.get_doc('Time Log', data.time_log)
+ time_log = frappe.get_all('Time Log', fields=["*"],
+ filters = {'name': data.time_log})[0]
return {
+ 'billable': time_log.billable,
'from_time': time_log.from_time,
'hours': time_log.hours,
'to_time': time_log.to_time,
'project': time_log.project,
+ 'task': time_log.task,
'activity_type': time_log.activity_type,
'operation': time_log.operation,
'operation_id': time_log.operation_id,
diff --git a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
index 55b51b7..e994199 100644
--- a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
+++ b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
@@ -1,16 +1,12 @@
import frappe
-from erpnext.manufacturing.doctype.production_order.production_order import add_timesheet_detail
-
def execute():
- for si in frappe.db.sql(""" select sales_invoice as name from `tabTime Sheet`
+ for time_sheet in frappe.db.sql(""" select sales_invoice, name, total_billing_amount from `tabTime Sheet`
where sales_invoice is not null and docstatus < 2""", as_dict=True):
- si_doc = frappe.get_doc('Sales Invoice', si.name)
- for item in si_doc.items:
- if item.time_log_batch:
- ts = si_doc.append('timesheets',{})
- ts.time_sheet = item.time_log_batch
- ts.billing_amount = frappe.db.get_value('Time Log Batch', item.time_log_batch, 'total_billing_amount')
- si_doc.ignore_validate_update_after_submit = True
- si_doc.update_time_sheet(ts.time_sheet)
+ si_doc = frappe.get_doc('Sales Invoice', time_sheet.sales_invoice)
+ ts = si_doc.append('timesheets',{})
+ ts.time_sheet = time_sheet.name
+ ts.billing_amount = time_sheet.total_billing_amount
+ si_doc.update_time_sheet(time_sheet.sales_invoice)
+ si_doc.flags.ignore_validate_update_after_submit = True
si_doc.save()
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/rename_advance_table_fields.py b/erpnext/patches/v7_0/rename_advance_table_fields.py
new file mode 100644
index 0000000..ba8b2da
--- /dev/null
+++ b/erpnext/patches/v7_0/rename_advance_table_fields.py
@@ -0,0 +1,15 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+ for dt in ("Sales Invoice Advance", "Purchase Invoice Advance"):
+ frappe.reload_doctype(dt)
+
+ frappe.db.sql("update `tab{0}` set reference_type = 'Journal Entry'".format(dt))
+
+ rename_field(dt, "journal_entry", "reference_name")
+ rename_field(dt, "jv_detail_no", "reference_row")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/set_naming_series_for_timesheet.py b/erpnext/patches/v7_0/set_naming_series_for_timesheet.py
index a052282..7c26e59 100644
--- a/erpnext/patches/v7_0/set_naming_series_for_timesheet.py
+++ b/erpnext/patches/v7_0/set_naming_series_for_timesheet.py
@@ -9,6 +9,7 @@
def execute():
frappe.reload_doc('projects', 'doctype','time_sheet')
frappe.reload_doc('projects', 'doctype', 'time_sheet_detail')
+ frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
make_property_setter('Time Sheet', "naming_series", "options", 'TS-', "Text")
make_property_setter('Time Sheet', "naming_series", "default", 'TS-', "Text")
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py b/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py
new file mode 100644
index 0000000..1c41ec8
--- /dev/null
+++ b/erpnext/patches/v7_0/update_prevdoc_values_for_supplier_quotation_item.py
@@ -0,0 +1,8 @@
+import frappe
+
+def execute():
+ frappe.reload_doctype('Supplier Quotation Item')
+ for data in frappe.db.sql(""" select prevdoc_docname, prevdoc_detail_docname, name
+ from `tabSupplier Quotation Item` where prevdoc_docname is not null""", as_dict=True):
+ frappe.db.set_value("Supplier Quotation Item", data.name, "material_request", data.prevdoc_docname)
+ frappe.db.set_value("Supplier Quotation Item", data.name, "material_request_item", data.prevdoc_detail_docname)
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index ccc0cfe..9736553 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -16,23 +16,27 @@
def onload(self):
"""Load project tasks for quick view"""
if not self.get('__unsaved') and not self.get("tasks"):
- for task in self.get_tasks():
- self.append("tasks", {
- "title": task.subject,
- "status": task.status,
- "start_date": task.exp_start_date,
- "end_date": task.exp_end_date,
- "description": task.description,
- "task_id": task.name
- })
+ self.load_tasks()
- self.set_onload('links', self.meta.get_links_setup())
self.set_onload('activity_summary', frappe.db.sql('''select activity_type, sum(hours) as total_hours
from `tabTime Sheet Detail` where project=%s group by activity_type order by total_hours desc''', self.name, as_dict=True))
def __setup__(self):
self.onload()
+ def load_tasks(self):
+ """Load `tasks` from the database"""
+ self.tasks = []
+ for task in self.get_tasks():
+ self.append("tasks", {
+ "title": task.subject,
+ "status": task.status,
+ "start_date": task.exp_start_date,
+ "end_date": task.exp_end_date,
+ "description": task.description,
+ "task_id": task.name
+ })
+
def get_tasks(self):
return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
@@ -147,19 +151,7 @@
frappe.sendmail(user.user, subject=_("Project Collaboration Invitation"), content=content.format(*messages))
user.welcome_email_sent=1
-
-@frappe.whitelist()
-def get_dashboard_data(name):
- '''load dashboard related data'''
- frappe.has_permission(doc=frappe.get_doc('Project', name), throw=True)
-
- from frappe.desk.notifications import get_open_count
- return {
- 'count': get_open_count('Project', name),
- 'timeline_data': get_timeline_data(name)
- }
-
-def get_timeline_data(name):
+def get_timeline_data(doctype, name):
'''Return timeline for attendance'''
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
from `tabTime Sheet Detail` where project=%s
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ce11c8a..1379048 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -4,6 +4,16 @@
erpnext.TransactionController = erpnext.taxes_and_totals.extend({
setup: function() {
this._super();
+
+ if(in_list(["Sales Invoice", "Purchase Invoice"], this.frm.doc.doctype)) {
+ this.frm.get_field('advances').grid.editable_fields = [
+ {fieldname: 'reference_name', columns: 2},
+ {fieldname: 'remarks', columns: 3},
+ {fieldname: 'advance_amount', columns: 3},
+ {fieldname: 'allocated_amount', columns: 3}
+ ];
+ }
+
frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["rate", "price_list_rate"]);
@@ -159,7 +169,6 @@
this.show_item_wise_taxes();
this.set_dynamic_labels();
this.setup_sms();
- this.make_show_payments_btn();
},
apply_default_taxes: function() {
@@ -195,22 +204,6 @@
var sms_man = new SMSManager(this.frm.doc);
},
- make_show_payments_btn: function() {
- var me = this;
- if (in_list(["Purchase Invoice", "Sales Invoice"], this.frm.doctype)) {
- if(this.frm.doc.outstanding_amount !== this.frm.doc.base_grand_total) {
- this.frm.add_custom_button(__("Payments"), function() {
- frappe.route_options = {
- "Journal Entry Account.reference_type": me.frm.doc.doctype,
- "Journal Entry Account.reference_name": me.frm.doc.name
- };
-
- frappe.set_route("List", "Journal Entry");
- }, __("View"));
- }
- }
- },
-
barcode: function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(d.barcode=="" || d.barcode==null) {
@@ -978,5 +971,32 @@
if(!this.item_selector) {
this.item_selector = new erpnext.ItemSelector({frm: this.frm});
}
+ },
+
+ get_advances: function() {
+ if(!this.frm.is_return) {
+ return this.frm.call({
+ method: "set_advances",
+ doc: this.frm.doc,
+ callback: function(r, rt) {
+ refresh_field("advances");
+ }
+ })
+ }
+ },
+
+ make_payment_entry: function() {
+ return frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
+ args: {
+ "dt": cur_frm.doc.doctype,
+ "dn": cur_frm.doc.name
+ },
+ callback: function(r) {
+ var doclist = frappe.model.sync(r.message);
+ frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+ // cur_frm.refresh_fields()
+ }
+ });
}
});
\ No newline at end of file
diff --git a/erpnext/public/js/stock_analytics.js b/erpnext/public/js/stock_analytics.js
index c64a2c9..28ff55d 100644
--- a/erpnext/public/js/stock_analytics.js
+++ b/erpnext/public/js/stock_analytics.js
@@ -1,211 +1,210 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-frappe.require("assets/erpnext/js/stock_grid_report.js", function() {
- erpnext.StockAnalytics = erpnext.StockGridReport.extend({
- init: function(wrapper, opts) {
- var args = {
- title: __("Stock Analytics"),
- page: wrapper,
- parent: $(wrapper).find('.layout-main'),
- page: wrapper.page,
- doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand",
- "Fiscal Year", "Serial No"],
- tree_grid: {
- show: true,
- parent_field: "parent_item_group",
- formatter: function(item) {
- if(!item.is_group) {
- return repl("<a \
- onclick='frappe.cur_grid_report.show_stock_ledger(\"%(value)s\")'>\
- %(value)s</a>", {
- value: item.name,
- });
- } else {
- return item.name;
- }
- }
- },
- }
-
- if(opts) $.extend(args, opts);
-
- this._super(args);
- },
- setup_columns: function() {
- var std_columns = [
- {id: "_check", name: __("Plot"), field: "_check", width: 30,
- formatter: this.check_formatter},
- {id: "name", name: __("Item"), field: "name", width: 300,
- formatter: this.tree_formatter},
- {id: "brand", name: __("Brand"), field: "brand", width: 100},
- {id: "stock_uom", name: __("UOM"), field: "stock_uom", width: 100},
- {id: "opening", name: __("Opening"), field: "opening", hidden: true,
- formatter: this.currency_formatter}
- ];
-
- this.make_date_range_columns();
- this.columns = std_columns.concat(this.columns);
- },
- filters: [
- {fieldtype:"Select", label: __("Value or Qty"), fieldname: "value_or_qty",
- options:[{label:__("Value"), value:"Value"}, {label:__("Quantity"), value:"Quantity"}],
- filter: function(val, item, opts, me) {
- return me.apply_zero_filter(val, item, opts, me);
- }},
- {fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand",
- default_value: __("Select Brand..."), filter: function(val, item, opts) {
- return val == opts.default_value || item.brand == val || item._show;
- }, link_formatter: {filter_input: "brand"}},
- {fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse",
- default_value: __("Select Warehouse...")},
- {fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
- {fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
- {fieldtype:"Select", label: __("Range"), fieldname: "range",
- options:[
- {label:__("Daily"), value:"Daily"},
- {label:__("Weekly"), value:"Weekly"},
- {label:__("Monthly"), value:"Monthly"},
- {label:__("Quarterly"), value:"Quarterly"},
- {label:__("Yearly"), value:"Yearly"},
- ]}
- ],
- setup_filters: function() {
- var me = this;
- this._super();
-
- this.trigger_refresh_on_change(["value_or_qty", "brand", "warehouse", "range"]);
-
- this.show_zero_check();
- this.setup_chart_check();
- },
- init_filter_values: function() {
- this._super();
- this.filter_inputs.range && this.filter_inputs.range.val('Monthly');
- },
- prepare_data: function() {
- var me = this;
-
- if(!this.data) {
- var items = this.prepare_tree("Item", "Item Group");
-
- me.parent_map = {};
- me.item_by_name = {};
- me.data = [];
-
- $.each(items, function(i, v) {
- var d = copy_dict(v);
-
- me.data.push(d);
- me.item_by_name[d.name] = d;
- if(d.parent_item_group) {
- me.parent_map[d.name] = d.parent_item_group;
- }
- me.reset_item_values(d);
- });
- this.set_indent();
- this.data[0].checked = true;
- } else {
- // otherwise, only reset values
- $.each(this.data, function(i, d) {
- me.reset_item_values(d);
- d["closing_qty_value"] = 0;
- });
- }
-
- this.prepare_balances();
- this.update_groups();
-
- },
- prepare_balances: function() {
- var me = this;
- var from_date = dateutil.str_to_obj(this.from_date);
- var to_date = dateutil.str_to_obj(this.to_date);
- var data = frappe.report_dump.data["Stock Ledger Entry"];
-
- this.item_warehouse = {};
- this.serialized_buying_rates = this.get_serialized_buying_rates();
-
- for(var i=0, j=data.length; i<j; i++) {
- var sl = data[i];
- sl.posting_datetime = sl.posting_date + " " + sl.posting_time;
- var posting_datetime = dateutil.str_to_obj(sl.posting_datetime);
-
- if(me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) {
- var item = me.item_by_name[sl.item_code];
- if(item.closing_qty_value==undefined) item.closing_qty_value = 0;
-
- if(me.value_or_qty!="Quantity") {
- var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
- var valuation_method = item.valuation_method ?
- item.valuation_method : sys_defaults.valuation_method;
- var is_fifo = valuation_method == "FIFO";
-
- if(sl.voucher_type=="Stock Reconciliation") {
- var diff = (sl.qty_after_transaction * sl.valuation_rate) - item.closing_qty_value;
- wh.fifo_stack = [[sl.qty_after_transaction, sl.valuation_rate, sl.posting_date]];
- wh.balance_qty = sl.qty_after_transaction;
- wh.balance_value = sl.valuation_rate * sl.qty_after_transaction;
- } else {
- var diff = me.get_value_diff(wh, sl, is_fifo);
- }
+erpnext.StockAnalytics = erpnext.StockGridReport.extend({
+ init: function(wrapper, opts) {
+ var args = {
+ title: __("Stock Analytics"),
+ page: wrapper,
+ parent: $(wrapper).find('.layout-main'),
+ page: wrapper.page,
+ doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand",
+ "Fiscal Year", "Serial No"],
+ tree_grid: {
+ show: true,
+ parent_field: "parent_item_group",
+ formatter: function(item) {
+ if(!item.is_group) {
+ return repl("<a \
+ onclick='frappe.cur_grid_report.show_stock_ledger(\"%(value)s\")'>\
+ %(value)s</a>", {
+ value: item.name,
+ });
} else {
- if(sl.voucher_type=="Stock Reconciliation") {
- var diff = sl.qty_after_transaction - item.closing_qty_value;
- } else {
- var diff = sl.qty;
- }
+ return item.name;
}
- if(posting_datetime < from_date) {
- item.opening += diff;
- } else if(posting_datetime <= to_date) {
- item[me.column_map[sl.posting_date].field] += diff;
- } else {
- break;
- }
-
- item.closing_qty_value += diff;
}
+ },
+ }
+
+ if(opts) $.extend(args, opts);
+
+ this._super(args);
+ },
+ setup_columns: function() {
+ var std_columns = [
+ {id: "_check", name: __("Plot"), field: "_check", width: 30,
+ formatter: this.check_formatter},
+ {id: "name", name: __("Item"), field: "name", width: 300,
+ formatter: this.tree_formatter},
+ {id: "brand", name: __("Brand"), field: "brand", width: 100},
+ {id: "stock_uom", name: __("UOM"), field: "stock_uom", width: 100},
+ {id: "opening", name: __("Opening"), field: "opening", hidden: true,
+ formatter: this.currency_formatter}
+ ];
+
+ this.make_date_range_columns();
+ this.columns = std_columns.concat(this.columns);
+ },
+ filters: [
+ {fieldtype:"Select", label: __("Value or Qty"), fieldname: "value_or_qty",
+ options:[{label:__("Value"), value:"Value"}, {label:__("Quantity"), value:"Quantity"}],
+ filter: function(val, item, opts, me) {
+ return me.apply_zero_filter(val, item, opts, me);
+ }},
+ {fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand",
+ default_value: __("Select Brand..."), filter: function(val, item, opts) {
+ return val == opts.default_value || item.brand == val || item._show;
+ }, link_formatter: {filter_input: "brand"}},
+ {fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse",
+ default_value: __("Select Warehouse...")},
+ {fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
+ {fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
+ {fieldtype:"Select", label: __("Range"), fieldname: "range",
+ options:[
+ {label:__("Daily"), value:"Daily"},
+ {label:__("Weekly"), value:"Weekly"},
+ {label:__("Monthly"), value:"Monthly"},
+ {label:__("Quarterly"), value:"Quarterly"},
+ {label:__("Yearly"), value:"Yearly"},
+ ]}
+ ],
+ setup_filters: function() {
+ var me = this;
+ this._super();
+
+ this.trigger_refresh_on_change(["value_or_qty", "brand", "warehouse", "range"]);
+
+ this.show_zero_check();
+ this.setup_chart_check();
+ },
+ init_filter_values: function() {
+ this._super();
+ this.filter_inputs.range && this.filter_inputs.range.val('Monthly');
+ },
+ prepare_data: function() {
+ var me = this;
+
+ if(!this.data) {
+ var items = this.prepare_tree("Item", "Item Group");
+
+ me.parent_map = {};
+ me.item_by_name = {};
+ me.data = [];
+
+ $.each(items, function(i, v) {
+ var d = copy_dict(v);
+
+ me.data.push(d);
+ me.item_by_name[d.name] = d;
+ if(d.parent_item_group) {
+ me.parent_map[d.name] = d.parent_item_group;
+ }
+ me.reset_item_values(d);
+ });
+ this.set_indent();
+ this.data[0].checked = true;
+ } else {
+ // otherwise, only reset values
+ $.each(this.data, function(i, d) {
+ me.reset_item_values(d);
+ d["closing_qty_value"] = 0;
+ });
+ }
+
+ this.prepare_balances();
+ this.update_groups();
+
+ },
+ prepare_balances: function() {
+ var me = this;
+ var from_date = dateutil.str_to_obj(this.from_date);
+ var to_date = dateutil.str_to_obj(this.to_date);
+ var data = frappe.report_dump.data["Stock Ledger Entry"];
+
+ this.item_warehouse = {};
+ this.serialized_buying_rates = this.get_serialized_buying_rates();
+
+ for(var i=0, j=data.length; i<j; i++) {
+ var sl = data[i];
+ sl.posting_datetime = sl.posting_date + " " + sl.posting_time;
+ var posting_datetime = dateutil.str_to_obj(sl.posting_datetime);
+
+ if(me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) {
+ var item = me.item_by_name[sl.item_code];
+ if(item.closing_qty_value==undefined) item.closing_qty_value = 0;
+
+ if(me.value_or_qty!="Quantity") {
+ var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
+ var valuation_method = item.valuation_method ?
+ item.valuation_method : sys_defaults.valuation_method;
+ var is_fifo = valuation_method == "FIFO";
+
+ if(sl.voucher_type=="Stock Reconciliation") {
+ var diff = (sl.qty_after_transaction * sl.valuation_rate) - item.closing_qty_value;
+ wh.fifo_stack = [[sl.qty_after_transaction, sl.valuation_rate, sl.posting_date]];
+ wh.balance_qty = sl.qty_after_transaction;
+ wh.balance_value = sl.valuation_rate * sl.qty_after_transaction;
+ } else {
+ var diff = me.get_value_diff(wh, sl, is_fifo);
+ }
+ } else {
+ if(sl.voucher_type=="Stock Reconciliation") {
+ var diff = sl.qty_after_transaction - item.closing_qty_value;
+ } else {
+ var diff = sl.qty;
+ }
+ }
+
+ if(posting_datetime < from_date) {
+ item.opening += diff;
+ } else if(posting_datetime <= to_date) {
+ item[me.column_map[sl.posting_date].field] += diff;
+ } else {
+ break;
+ }
+
+ item.closing_qty_value += diff;
}
- },
- update_groups: function() {
- var me = this;
- $.each(this.data, function(i, item) {
- // update groups
- if(!item.is_group && me.apply_filter(item, "brand")) {
- var balance = item.opening;
- $.each(me.columns, function(i, col) {
- if(col.formatter==me.currency_formatter && !col.hidden) {
- item[col.field] = balance + item[col.field];
- balance = item[col.field];
+ }
+ },
+ update_groups: function() {
+ var me = this;
+ $.each(this.data, function(i, item) {
+ // update groups
+ if(!item.is_group && me.apply_filter(item, "brand")) {
+ var balance = item.opening;
+ $.each(me.columns, function(i, col) {
+ if(col.formatter==me.currency_formatter && !col.hidden) {
+ item[col.field] = balance + item[col.field];
+ balance = item[col.field];
+ }
+ });
+
+ var parent = me.parent_map[item.name];
+ while(parent) {
+ parent_group = me.item_by_name[parent];
+ $.each(me.columns, function(c, col) {
+ if (col.formatter == me.currency_formatter) {
+ parent_group[col.field] =
+ flt(parent_group[col.field])
+ + flt(item[col.field]);
}
});
-
- var parent = me.parent_map[item.name];
- while(parent) {
- parent_group = me.item_by_name[parent];
- $.each(me.columns, function(c, col) {
- if (col.formatter == me.currency_formatter) {
- parent_group[col.field] =
- flt(parent_group[col.field])
- + flt(item[col.field]);
- }
- });
- parent = me.parent_map[parent];
- }
+ parent = me.parent_map[parent];
}
- });
- },
- show_stock_ledger: function(item_code) {
- frappe.route_options = {
- item_code: item_code,
- from_date: this.from_date,
- to_date: this.to_date
- };
- frappe.set_route("query-report", "Stock Ledger");
- }
- });
+ }
+ });
+ },
+ show_stock_ledger: function(item_code) {
+ frappe.route_options = {
+ item_code: item_code,
+ from_date: this.from_date,
+ to_date: this.to_date
+ };
+ frappe.set_route("query-report", "Stock Ledger");
+ }
});
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 591b9c1..ee3f6e6 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -10,7 +10,7 @@
from frappe.desk.reportview import build_match_conditions
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.utilities.address_and_contact import load_address_and_contact
-from erpnext.accounts.party import validate_party_accounts, get_timeline_data
+from erpnext.accounts.party import validate_party_accounts, get_timeline_data # keep this
from erpnext.accounts.party_status import get_party_status
class Customer(TransactionBase):
@@ -20,7 +20,6 @@
def onload(self):
"""Load address and contacts in `__onload`"""
load_address_and_contact(self, "customer")
- self.set_onload('links', self.meta.get_links_setup())
def autoname(self):
cust_master_name = frappe.defaults.get_global_default('cust_master_name')
@@ -129,17 +128,6 @@
.format(set_field=set_field), ({"newdn": newdn}))
-@frappe.whitelist()
-def get_dashboard_data(name):
- '''load dashboard related data'''
- frappe.has_permission(doc=frappe.get_doc('Customer', name), throw=True)
-
- from frappe.desk.notifications import get_open_count
- return {
- 'count': get_open_count('Customer', name),
- 'timeline_data': get_timeline_data('Customer', name),
- }
-
def get_customer_list(doctype, txt, searchfield, start, page_len, filters):
if frappe.db.get_default("cust_master_name") == "Customer Name":
fields = ["name", "customer_group", "territory"]
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index ce9c958..5f1a5c7 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -76,11 +76,12 @@
if(flt(doc.per_billed)==0) {
cur_frm.add_custom_button(__('Payment Request'), this.make_payment_request, __("Make"));
- cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry, __("Make"));
+ cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __("Make"));
}
// maintenance
- if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
+ if(flt(doc.per_delivered, 2) < 100 &&
+ ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
cur_frm.add_custom_button(__('Maintenance Visit'), this.make_maintenance_visit, __("Make"));
cur_frm.add_custom_button(__('Maintenance Schedule'), this.make_maintenance_schedule, __("Make"));
}
@@ -157,19 +158,6 @@
})
},
- make_bank_entry: function() {
- return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order",
- args: {
- "dt": "Sales Order",
- "dn": cur_frm.doc.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
- },
make_purchase_order: function(){
var dialog = new frappe.ui.Dialog({
title: __("For Supplier"),
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 8103756..8fcd89d 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -441,7 +441,7 @@
def postprocess(source, target):
set_missing_values(source, target)
#Get the advance paid Journal Entries in Sales Invoice Advance
- target.get_advances()
+ target.set_advances()
def set_missing_values(source, target):
target.is_pos = 0
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 2a18286..ff12f68 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -136,6 +136,8 @@
["default_expense_account", {"root_type": "Expense"}],
["default_income_account", {"root_type": "Income"}],
["round_off_account", {"root_type": "Expense"}],
+ ["write_off_account", {"root_type": "Expense"}],
+ ["exchange_gain_loss_account", {"root_type": "Expense"}],
["accumulated_depreciation_account", {"root_type": "Asset"}],
["depreciation_expense_account", {"root_type": "Expense"}],
["disposal_account", {"report_type": "Profit and Loss"}],
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 9140534..25e0cfb 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -499,6 +499,58 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "fieldname": "write_off_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Write Off Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "exchange_gain_loss_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Exchange Gain / Loss Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1325,6 +1377,7 @@
"hide_toolbar": 0,
"icon": "icon-building",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
@@ -1332,7 +1385,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-05-16 15:24:47.178826",
+ "modified": "2016-06-26 09:08:50.476200",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 6b66324..65fb420 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -13,7 +13,7 @@
class Company(Document):
def onload(self):
- self.get("__onload").transactions_exist = self.check_if_transactions_exist()
+ self.get("__onload")["transactions_exist"] = self.check_if_transactions_exist()
def check_if_transactions_exist(self):
exists = False
diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py
index 1de376c..ffcac98 100644
--- a/erpnext/setup/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/setup_wizard/setup_wizard.py
@@ -21,11 +21,11 @@
install_fixtures.install(args.get("country"))
update_setup_wizard_access()
+ create_price_lists(args)
create_fiscal_year_and_company(args)
create_users(args)
set_defaults(args)
create_territories()
- create_price_lists(args)
create_feed_and_todo()
create_email_digest()
create_letter_head(args)
@@ -88,10 +88,23 @@
'chart_of_accounts': args.get(('chart_of_accounts')),
'domain': args.get('domain')
}).insert()
-
+
+ #Enable shopping cart
+ enable_shopping_cart(args)
+
# Bank Account
create_bank_account(args)
-
+
+def enable_shopping_cart(args):
+ frappe.get_doc({
+ "doctype": "Shopping Cart Settings",
+ "enabled": 1,
+ 'company': args.get('company_name').strip(),
+ 'price_list': frappe.db.get_value("Price List", {"selling": 1}),
+ 'default_customer_group': _("Individual"),
+ 'quotation_series': "QTN-",
+ }).insert()
+
def create_bank_account(args):
if args.get("bank_account"):
company_name = args.get('company_name').strip()
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 367d114..2f346bc 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -28,7 +28,6 @@
def onload(self):
super(Item, self).onload()
self.set_onload('sle_exists', self.check_if_sle_exists())
- self.set_onload('links', self.meta.get_links_setup())
def autoname(self):
if frappe.db.get_default("item_naming_by")=="Naming Series":
@@ -632,19 +631,7 @@
frappe.throw(_("Item variant {0} exists with same attributes")
.format(variant), ItemVariantExistsError)
-
-@frappe.whitelist()
-def get_dashboard_data(name):
- '''load dashboard related data'''
- frappe.has_permission(doc=frappe.get_doc('Item', name), throw=True)
-
- from frappe.desk.notifications import get_open_count
- return {
- 'count': get_open_count('Item', name),
- 'timeline_data': get_timeline_data(name),
- }
-
-def get_timeline_data(name):
+def get_timeline_data(doctype, name):
'''returns timeline data based on stock ledger entry'''
return dict(frappe.db.sql('''select unix_timestamp(posting_date), count(*)
from `tabStock Ledger Entry` where item_code=%s
diff --git a/erpnext/stock/doctype/item/item_links.py b/erpnext/stock/doctype/item/item_links.py
index 5586214..68280dc 100644
--- a/erpnext/stock/doctype/item/item_links.py
+++ b/erpnext/stock/doctype/item/item_links.py
@@ -5,6 +5,7 @@
'non_standard_fieldnames': {
'Production Order': 'production_item',
'Product Bundle': 'new_item_code',
+ 'BOM': 'item',
'Batch': 'item'
},
'transactions': [
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 68ce231..1f0fe34 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -40,8 +40,11 @@
},
refresh: function(doc) {
+ var me = this;
this._super();
+ this.frm.dashboard.reset();
+
if(doc.docstatus==0) {
cur_frm.add_custom_button(__("Get Items from BOM"),
cur_frm.cscript.get_items_from_bom, "icon-sitemap", "btn-default");
@@ -49,6 +52,8 @@
if(doc.docstatus == 1 && doc.status != 'Stopped') {
+ this.frm.dashboard.show_dashboard();
+
if(flt(doc.per_ordered, 2) < 100) {
// make
if(doc.material_request_type === "Material Transfer" && doc.status === "Submitted")
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index ef1c574..3de62c1 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -321,9 +321,8 @@
"Material Request Item": {
"doctype": "Supplier Quotation Item",
"field_map": {
- "name": "prevdoc_detail_docname",
- "parent": "prevdoc_docname",
- "parenttype": "prevdoc_doctype"
+ "name": "material_request_item",
+ "parent": "material_request"
}
}
}, target_doc, postprocess)
diff --git a/erpnext/stock/doctype/material_request/material_request_links.py b/erpnext/stock/doctype/material_request/material_request_links.py
new file mode 100644
index 0000000..7f12490
--- /dev/null
+++ b/erpnext/stock/doctype/material_request/material_request_links.py
@@ -0,0 +1,15 @@
+from frappe import _
+
+links = {
+ 'fieldname': 'material_request',
+ 'non_standard_fieldnames': {
+ 'Supplier Quotation': 'prevdoc_detail_docname',
+ 'Purchase Order': 'prevdoc_detail_docname',
+ },
+ 'transactions': [
+ {
+ 'label': _('Documents'),
+ 'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order']
+ },
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 62ecee6..f10b634 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -2197,6 +2197,31 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "fieldname": "language",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Print Language",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"fieldname": "other_details",
"fieldtype": "HTML",
"hidden": 1,
@@ -2419,7 +2444,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-06-09 15:11:04.042369",
+ "modified": "2016-06-30 13:42:23.310309",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",
diff --git a/erpnext/stock/page/stock_analytics/stock_analytics.js b/erpnext/stock/page/stock_analytics/stock_analytics.js
index f86201f..6deeb7a 100644
--- a/erpnext/stock/page/stock_analytics/stock_analytics.js
+++ b/erpnext/stock/page/stock_analytics/stock_analytics.js
@@ -1,16 +1,16 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-frappe.require("assets/erpnext/js/stock_analytics.js", function() {
- frappe.pages['stock-analytics'].on_page_load = function(wrapper) {
- frappe.ui.make_app_page({
- parent: wrapper,
- title: __('Stock Analytics'),
- single_column: true
- });
-
+frappe.pages['stock-analytics'].on_page_load = function(wrapper) {
+ frappe.ui.make_app_page({
+ parent: wrapper,
+ title: __('Stock Analytics'),
+ single_column: true
+ });
+
+ frappe.require(["assets/erpnext/js/stock_grid_report.js",
+ "assets/erpnext/js/stock_analytics.js"], function() {
new erpnext.StockAnalytics(wrapper);
-
frappe.breadcrumbs.add("Stock")
- };
-});
+ });
+};
diff --git a/erpnext/stock/report/items_to_be_requested/items_to_be_requested.json b/erpnext/stock/report/items_to_be_requested/items_to_be_requested.json
index d2f42d4..616eae1 100644
--- a/erpnext/stock/report/items_to_be_requested/items_to_be_requested.json
+++ b/erpnext/stock/report/items_to_be_requested/items_to_be_requested.json
@@ -1,17 +1,19 @@
{
- "apply_user_permissions": 1,
- "creation": "2013-08-20 15:08:10",
- "docstatus": 0,
- "doctype": "Report",
- "idx": 1,
- "is_standard": "Yes",
- "modified": "2014-06-03 07:18:17.128919",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Items To Be Requested",
- "owner": "Administrator",
+ "add_total_row": 0,
+ "apply_user_permissions": 1,
+ "creation": "2013-08-20 15:08:10",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 1,
+ "is_standard": "Yes",
+ "modified": "2016-06-29 17:39:54.122679",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Items To Be Requested",
+ "owner": "Administrator",
"query": "SELECT\n tabBin.item_code as \"Item:Link/Item:120\",\n tabBin.warehouse as \"Warehouse:Link/Warehouse:120\",\n tabBin.actual_qty as \"Actual:Float:90\",\n tabBin.indented_qty as \"Requested:Float:90\",\n tabBin.reserved_qty as \"Reserved:Float:90\",\n tabBin.ordered_qty as \"Ordered:Float:90\",\n tabBin.projected_qty as \"Projected:Float:90\"\nFROM\n tabBin, tabItem\nWHERE\n tabBin.item_code = tabItem.name\n AND tabBin.projected_qty < 0\nORDER BY\n tabBin.projected_qty ASC",
- "ref_doctype": "Item",
- "report_name": "Items To Be Requested",
+ "ref_doctype": "Material Request",
+ "report_name": "Items To Be Requested",
"report_type": "Query Report"
}
\ No newline at end of file
diff --git a/erpnext/templates/pages/demo.html b/erpnext/templates/pages/demo.html
new file mode 100644
index 0000000..e1d98a1
--- /dev/null
+++ b/erpnext/templates/pages/demo.html
@@ -0,0 +1,70 @@
+{% extends "templates/web.html" %}
+
+{% block script %}
+<script>
+$(document).ready(function() {
+ $("#login_btn").click(function() {
+ var me = this;
+ $(this).html("Logging In...").prop("disabled", true);
+ frappe.call({
+ "method": "login",
+ args: {
+ usr: "demo@erpnext.com",
+ pwd: "demo",
+ lead_email: $("#lead-email").val(),
+ },
+ callback: function(r) {
+ $(me).prop("disabled", false);
+ if(r.exc) {
+ alert("Error, please contact support@erpnext.com");
+ } else {
+ console.log("Logged In");
+ window.location.href = "desk";
+ }
+ }
+ })
+ return false;
+ })
+ .prop("disabled", false);
+})
+
+</script>
+{% endblock %}
+
+{% block style %}
+<style>
+ footer, .navbar {
+ display: none;
+ }
+ .page-content {
+ right: 0%;
+ width: 100%;
+ }
+</style>
+{% endblock %}
+
+{% block title %}
+{{ _("Login") }}
+{% endblock %}
+
+{% block page_content %}
+
+<!-- no-header -->
+<div class="page-hero text-center">
+ <img src="/assets/erpnext_demo/images/erp-icon-box.svg" style="max-width: 100px; max-height: 100px;">
+ <h1>ERPNext Demo</h1>
+ <p style="margin-top: 60px;">
+ <input id="lead-email" type="email"
+ class="form-control" placeholder="Your Email Id (optional)"
+ style="width: 75%; max-width: 400px; margin: auto;">
+ </p>
+
+ <button type="submit" id="login_btn" class="btn btn-default">Launch Demo</button>
+
+ <hr style="margin: 60px 0px;">
+ <p class="text-muted small">Some functionality is disabled for the demo app. The demo data will be cleared regulary.
+ <br class="hidden-xs">
+ To start your free ERPNext account, <a href="https://erpnext.com/signup?plan=Free-Solo">click here</a></p>
+</div>
+
+{% endblock %}
diff --git a/erpnext/templates/pages/rfq.py b/erpnext/templates/pages/rfq.py
index 4c488d9..decae41 100644
--- a/erpnext/templates/pages/rfq.py
+++ b/erpnext/templates/pages/rfq.py
@@ -9,6 +9,7 @@
def get_context(context):
context.no_cache = 1
+ context.show_sidebar = True
context.doc = frappe.get_doc(frappe.form_dict.doctype, frappe.form_dict.name)
context.parents = frappe.form_dict.parents
context.doc.supplier = get_supplier()