diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 1b933ce2c8..1ba9221b38 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -21,9 +21,11 @@ "in_filter": 0, "in_list_view": 1, "label": "Make Accounting Entry For Every Stock Movement", + "length": 0, "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -43,9 +45,11 @@ "in_filter": 0, "in_list_view": 1, "label": "Accounts Frozen Upto", + "length": 0, "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -65,10 +69,12 @@ "in_filter": 0, "in_list_view": 1, "label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries", + "length": 0, "no_copy": 0, "options": "Role", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -88,10 +94,12 @@ "in_filter": 0, "in_list_view": 1, "label": "Credit Controller", + "length": 0, "no_copy": 0, "options": "Role", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -110,10 +118,12 @@ "in_filter": 0, "in_list_view": 0, "label": "Check Supplier Invoice Number Uniqueness", + "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, @@ -131,7 +141,8 @@ "is_submittable": 0, "issingle": 1, "istable": 0, - "modified": "2015-07-14 00:51:48.095525", + "max_attachments": 0, + "modified": "2015-12-24 21:42:01.274459", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 670661c01f..7deca523c9 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -508,7 +508,7 @@ class JournalEntry(AccountsController): 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): +def get_default_bank_cash_account(company, voucher_type, 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) @@ -516,16 +516,18 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None): account.update({"balance": get_balance_on(account.get("account"))}) return account - if voucher_type=="Bank Entry": - 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": - account = frappe.db.get_value("Company", company, "default_cash_account") - if not account: - account = frappe.db.get_value("Account", - {"company": company, "account_type": "Cash", "is_group": 0}) + if not account: + if voucher_type=="Bank Entry": + 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": + account = frappe.db.get_value("Company", company, "default_cash_account") + if not account: + account = frappe.db.get_value("Account", + {"company": company, "account_type": "Cash", "is_group": 0}) if account: account_details = frappe.db.get_value("Account", account, @@ -538,7 +540,7 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None): } @frappe.whitelist() -def get_payment_entry_against_order(dt, dn): +def get_payment_entry_against_order(dt, dn, amount=None, journal_entry=False, bank_account=None): ref_doc = frappe.get_doc(dt, dn) if flt(ref_doc.per_billed, 2) > 0: @@ -556,10 +558,11 @@ def get_payment_entry_against_order(dt, dn): party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company) party_account_currency = get_account_currency(party_account) - if party_account_currency == ref_doc.company_currency: - amount = flt(ref_doc.base_grand_total) - flt(ref_doc.advance_paid) - else: - amount = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid) + if not amount: + if party_account_currency == ref_doc.company_currency: + amount = flt(ref_doc.base_grand_total) - flt(ref_doc.advance_paid) + else: + amount = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid) return get_payment_entry(ref_doc, { "party_type": party_type, @@ -569,11 +572,13 @@ def get_payment_entry_against_order(dt, dn): "amount_field_bank": amount_field_bank, "amount": amount, "remarks": 'Advance Payment received against {0} {1}'.format(dt, dn), - "is_advance": "Yes" + "is_advance": "Yes", + "bank_account": bank_account, + "journal_entry": journal_entry }) @frappe.whitelist() -def get_payment_entry_against_invoice(dt, dn): +def get_payment_entry_against_invoice(dt, dn, amount=None, journal_entry=False, bank_account=None): ref_doc = frappe.get_doc(dt, dn) if dt == "Sales Invoice": party_type = "Customer" @@ -597,9 +602,11 @@ def get_payment_entry_against_invoice(dt, dn): "party_account_currency": ref_doc.party_account_currency, "amount_field_party": amount_field_party, "amount_field_bank": amount_field_bank, - "amount": abs(ref_doc.outstanding_amount), + "amount": amount if amount else abs(ref_doc.outstanding_amount), "remarks": 'Payment received against {0} {1}. {2}'.format(dt, dn, ref_doc.remarks), - "is_advance": "No" + "is_advance": "No", + "bank_account": bank_account, + "journal_entry": journal_entry }) def get_payment_entry(ref_doc, args): @@ -607,14 +614,14 @@ def get_payment_entry(ref_doc, args): exchange_rate = get_exchange_rate(args.get("party_account"), args.get("party_account_currency"), ref_doc.company, ref_doc.doctype, ref_doc.name) - jv = frappe.new_doc("Journal Entry") - jv.update({ + je = frappe.new_doc("Journal Entry") + je.update({ "voucher_type": "Bank Entry", "company": ref_doc.company, "remark": args.get("remarks") }) - party_row = jv.append("accounts", { + party_row = je.append("accounts", { "account": args.get("party_account"), "party_type": args.get("party_type"), "party": ref_doc.get(args.get("party_type").lower()), @@ -631,8 +638,10 @@ def get_payment_entry(ref_doc, args): "reference_name": ref_doc.name }) - bank_row = jv.append("accounts") - bank_account = get_default_bank_cash_account(ref_doc.company, "Bank Entry") + 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")) if bank_account: bank_row.update(bank_account) bank_row.exchange_rate = get_exchange_rate(bank_account["account"], @@ -648,12 +657,12 @@ def get_payment_entry(ref_doc, args): # set multi currency check if party_row.account_currency != ref_doc.company_currency \ or (bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency): - jv.multi_currency = 1 + je.multi_currency = 1 - jv.set_amounts_in_company_currency() - jv.set_total_debit_credit() - - return jv.as_dict() + je.set_amounts_in_company_currency() + je.set_total_debit_credit() + + return je if args.get("journal_entry") else je.as_dict() @frappe.whitelist() def get_opening_accounts(company): diff --git a/erpnext/accounts/doctype/payment_gateway/__init__.py b/erpnext/accounts/doctype/payment_gateway/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/payment_gateway/payment_gateway.json b/erpnext/accounts/doctype/payment_gateway/payment_gateway.json new file mode 100644 index 0000000000..9b480665c6 --- /dev/null +++ b/erpnext/accounts/doctype/payment_gateway/payment_gateway.json @@ -0,0 +1,118 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:gateway", + "creation": "2015-12-15 22:26:45.221162", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "gateway", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Gateway", + "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, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2015-12-29 12:04:17.371619", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Gateway", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "System Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Accounts Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 + } + ], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_gateway/payment_gateway.py b/erpnext/accounts/doctype/payment_gateway/payment_gateway.py new file mode 100644 index 0000000000..80799e311b --- /dev/null +++ b/erpnext/accounts/doctype/payment_gateway/payment_gateway.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 PaymentGateway(Document): + pass \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_gateway/test_payment_gateway.py b/erpnext/accounts/doctype/payment_gateway/test_payment_gateway.py new file mode 100644 index 0000000000..2faf1a7fb4 --- /dev/null +++ b/erpnext/accounts/doctype/payment_gateway/test_payment_gateway.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +# test_records = frappe.get_test_records('Payment Gateway') + +class TestPaymentGateway(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/payment_gateway_account/__init__.py b/erpnext/accounts/doctype/payment_gateway_account/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js new file mode 100644 index 0000000000..c9bdc9b80a --- /dev/null +++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js @@ -0,0 +1,6 @@ +cur_frm.cscript.refresh = function(doc, dt, dn){ + if(!doc.__islocal){ + var df = frappe.meta.get_docfield(doc.doctype, "gateway", doc.name); + df.read_only = 1; + } +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json new file mode 100644 index 0000000000..c3a47724fb --- /dev/null +++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json @@ -0,0 +1,291 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "creation": "2015-12-23 21:31:52.699821", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "gateway_acount_details", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Gateway Acount Details", + "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": "is_default", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Is Default", + "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": "gateway", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Gateway", + "length": 0, + "no_copy": 0, + "options": "Payment Gateway", + "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": "payment_account", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment 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": "currency", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Currency", + "length": 0, + "no_copy": 0, + "options": "payment_account.account_currency", + "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_request_message", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment Request Message", + "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": "message", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 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": "payment_url_message", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment URL Message", + "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": "payment_success_url", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment Success URL", + "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, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2016-01-11 05:55:41.117089", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Gateway Account", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py new file mode 100644 index 0000000000..dd971ad5d2 --- /dev/null +++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py @@ -0,0 +1,24 @@ +# -*- 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 PaymentGatewayAccount(Document): + def autoname(self): + self.name = self.gateway + " - " + self.currency + + def validate(self): + self.update_default_payment_gateway() + self.set_as_default_if_not_set() + + def update_default_payment_gateway(self): + if self.is_default: + frappe.db.sql("""update `tabPayment Gateway Account` set is_default = 0 + where is_default = 1 """) + + def set_as_default_if_not_set(self): + if not frappe.db.get_value("Payment Gateway Account", {"is_default": 1, "name": ("!=", self.name)}, "name"): + self.is_default = 1 diff --git a/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py b/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py new file mode 100644 index 0000000000..84c3bc4a60 --- /dev/null +++ b/erpnext/accounts/doctype/payment_gateway_account/test_payment_gateway_account.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +# test_records = frappe.get_test_records('Payment Gateway Account') + +class TestPaymentGatewayAccount(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/payment_request/__init__.py b/erpnext/accounts/doctype/payment_request/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js new file mode 100644 index 0000000000..b28a889be8 --- /dev/null +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -0,0 +1,43 @@ +cur_frm.add_fetch("payment_gateway", "payment_account", "payment_account") +cur_frm.add_fetch("payment_gateway", "gateway", "gateway") +cur_frm.add_fetch("payment_gateway", "message", "message") +cur_frm.add_fetch("payment_gateway", "payment_url_message", "payment_url_message") +cur_frm.add_fetch("payment_gateway", "payment_success_url", "payment_success_url") + +frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){ + if (frm.doc.reference_doctype) { + frappe.call({ + method:"erpnext.accounts.doctype.payment_request.payment_request.get_print_format_list", + args: {"ref_doctype": frm.doc.reference_doctype}, + callback:function(r){ + set_field_options("print_format", r.message["print_format"]) + } + }) + } +}) + +frappe.ui.form.on("Payment Request", "refresh", function(frm) { + frm.add_custom_button(__('Resend Payment Email'), function(){ + frappe.call({ + method:"erpnext.accounts.doctype.payment_request.payment_request.resend_payment_email", + args: {"docname": frm.doc.name}, + freeze: true, + freeze_message: __("Sending"), + callback:function(r){ + if(!r.exc) { + frappe.msgprint(__("Message Sent")) + } + } + }) + }) + + frm.add_custom_button(__("Show Paypal Express Payment"), function() { + frappe.route_options = { + "Paypal Express Payment.reference_doctype": frm.doc.doctype, + "Paypal Express Payment.reference_docname": frm.doc.name + }; + + frappe.set_route("List", "Paypal Express Payment"); + }); +}) + diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json new file mode 100644 index 0000000000..7655b5872e --- /dev/null +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -0,0 +1,678 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "PR.######", + "creation": "2015-12-15 22:23:24.745065", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "payment_details", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment Details", + "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": "amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "2", + "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": "currency", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Currency", + "length": 0, + "no_copy": 0, + "options": "Currency", + "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.reference_doctype==\"Sales Order\"", + "fieldname": "make_sales_invoice", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Make Sales Invoice", + "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_5", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 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, + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Status", + "length": 0, + "no_copy": 0, + "options": "\nDraft\nInitiated\nPaid\nFailed\nCancelled", + "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": "section_break_7", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 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": "payment_gateway", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment Gateway", + "length": 0, + "no_copy": 0, + "options": "Payment Gateway 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": "payment_success_url", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment Success URL", + "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_9", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 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": "gateway", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Gateway", + "length": 0, + "no_copy": 0, + "options": "payment_gateway.gateway", + "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_account", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment Account", + "length": 0, + "no_copy": 0, + "options": "payment_gateway.payment_account", + "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": "recipient_and_message", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Recipient and Message", + "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, + "default": "", + "fieldname": "print_format", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Print Format", + "length": 0, + "no_copy": 0, + "options": "", + "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": "mute_email", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Mute Email", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 1, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "email_to", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Email To", + "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": 1, + "bold": 0, + "collapsible": 0, + "fieldname": "subject", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Subject", + "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": "message", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Message", + "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": "payment_url_message", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Payment URL Message", + "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": "payment_url", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "payment_url", + "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": "reference_details", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Details", + "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": "reference_doctype", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Doctype", + "length": 0, + "no_copy": 1, + "options": "DocType", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 1, + "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, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Name", + "length": 0, + "no_copy": 1, + "options": "reference_doctype", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 1, + "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, + "in_filter": 0, + "in_list_view": 0, + "label": "Amended From", + "length": 0, + "no_copy": 1, + "options": "Payment Request", + "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 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2016-01-11 05:49:28.342786", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Request", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 1, + "apply_user_permissions": 0, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "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": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + } + ], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ 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 new file mode 100644 index 0000000000..23fd5a8952 --- /dev/null +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -0,0 +1,238 @@ +# -*- 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 import _ +from frappe.model.document import Document +from frappe.utils import flt, nowdate, get_url, cstr +from erpnext.accounts.party import get_party_account +from erpnext.accounts.utils import get_account_currency, get_balance_on +from erpnext.accounts.doctype.journal_entry.journal_entry import (get_payment_entry_against_invoice, +get_payment_entry_against_order) + +from itertools import chain + +class PaymentRequest(Document): + def validate(self): + self.validate_payment_gateway_account() + self.validate_payment_request() + self.validate_currency() + + def validate_payment_request(self): + if frappe.db.get_value("Payment Request", {"reference_name": self.reference_name, + "name": ("!=", self.name), "status": ("not in", ["Initiated", "Paid"]), "docstatus": 1}, "name"): + frappe.throw(_("Payment Request already exists {0}".fomart(self.reference_name))) + + def validate_currency(self): + ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) + if ref_doc.currency != frappe.db.get_value("Account", self.payment_account, "account_currency"): + frappe.throw(_("Transaction currency must be same as Payment Gateway currency")) + + def validate_payment_gateway_account(self): + if not self.payment_gateway: + frappe.throw(_("Payment Gateway Account is not configured")) + + def validate_payment_gateway(self): + if self.gateway == "PayPal": + if not frappe.db.get_value("PayPal Settings", None, "api_username"): + if not frappe.conf.paypal_username: + frappe.throw(_("PayPal Settings missing")) + + + def on_submit(self): + if not self.mute_email: + self.send_payment_request() + self.send_email() + + self.make_communication_entry() + + def on_cancel(self): + self.set_as_cancelled() + + def on_update_after_submit(self): + pass + + def set_status(self): + pass + + def get_payment_url(self): + pass + + def make_invoice(self): + if self.make_sales_invoice: + from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice + si = make_sales_invoice(self.reference_name, ignore_permissions=True) + si = si.insert(ignore_permissions=True) + si.submit() + + def send_payment_request(self): + self.payment_url = get_url("/api/method/erpnext.accounts.doctype.payment_request.payment_request.generate_payment_request?name={0}".format(self.name)) + if self.payment_url: + frappe.db.set_value(self.doctype, self.name, "status", "Initiated") + + def set_as_paid(self): + if frappe.session.user == "Guest": + frappe.set_user("Administrator") + + jv = self.create_journal_entry() + self.make_invoice() + + return jv + + def create_journal_entry(self): + """create entry""" + payment_details = { + "amount": self.amount, + "journal_entry": True, + "bank_account": self.payment_account + } + + frappe.flags.ignore_account_permission = True + + if self.reference_doctype == "Sales Order": + jv = get_payment_entry_against_order(self.reference_doctype, self.reference_name,\ + amount=self.amount, 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=self.amount, journal_entry=True, bank_account=self.payment_account) + + jv.update({ + "voucher_type": "Journal Entry", + "posting_date": nowdate() + }) + jv.insert(ignore_permissions=True) + jv.submit() + + #set status as paid for Payment Request + frappe.db.set_value(self.doctype, self.name, "status", "Paid") + + return jv + + def send_email(self): + """send email with payment link""" + frappe.sendmail(recipients=self.email_to, sender=None, subject=self.subject, + message=self.get_message(), attachments=[frappe.attach_print(self.reference_doctype, + self.reference_name, file_name=self.reference_name, print_format=self.print_format)]) + + def get_message(self): + """return message with payment gateway link""" + return cstr(self.message) + " {1}".format(self.payment_url, \ + self.payment_url_message or _(" Click here to pay")) + + def set_failed(self): + pass + + def set_as_cancelled(self): + frappe.db.set_value(self.doctype, self.name, "status", "Cancelled") + + def make_communication_entry(self): + """Make communication entry""" + comm = frappe.get_doc({ + "doctype":"Communication", + "subject": self.subject, + "content": self.get_message(), + "sent_or_received": "Sent", + "reference_doctype": self.reference_doctype, + "reference_name": self.reference_name + }) + comm.insert(ignore_permissions=True) + + def get_payment_success_url(self): + return self.payment_success_url + +@frappe.whitelist(allow_guest=True) +def make_payment_request(**args): + """Make payment request""" + + args = frappe._dict(args) + ref_doc = frappe.get_doc(args.dt, args.dn) + gateway_account = get_gateway_details(args) + + pr = frappe.new_doc("Payment Request") + pr.update({ + "payment_gateway": gateway_account.name, + "gateway": gateway_account.gateway, + "payment_account": gateway_account.payment_account, + "currency": ref_doc.currency, + "make_sales_invoice": args.cart or 0, + "amount": get_amount(ref_doc, args.dt), + "mute_email": args.mute_email or 0, + "email_to": args.recipient_id or "", + "subject": "Payment Request for %s"%args.dn, + "message": gateway_account.message, + "payment_url_message": gateway_account.payment_url_message, + "payment_success_url": gateway_account.payment_success_url, + "reference_doctype": args.dt, + "reference_name": args.dn + }) + + if args.return_doc: + return pr + + if args.submit_doc: + pr.insert(ignore_permissions=True) + pr.submit() + + if args.cart: + generate_payment_request(pr.name) + frappe.db.commit() + + if not args.cart: + return pr + + return pr.as_dict() + +def get_amount(ref_doc, dt): + """get amount based on doctype""" + if dt == "Sales Order": + amount = flt(ref_doc.base_grand_total) - flt(ref_doc.advance_paid) + + if dt == "Sales Invoice": + amount = abs(ref_doc.outstanding_amount) + + if amount > 0: + return amount + else: + frappe.throw(_("Payment Entry is already created")) + +def get_gateway_details(args): + """return gateway and payment account of default payment gateway""" + if args.payemnt_gateway: + gateway_account = frappe.db.get_value("Payment Gateway Account", args.payemnt_gateway, + ["name", "gateway", "payment_account", "message", "payment_url_message", "payment_success_url"], + as_dict=1) + + gateway_account = frappe.db.get_value("Payment Gateway Account", {"is_default": 1}, + ["name", "gateway", "payment_account", "message", "payment_url_message", "payment_success_url"], + as_dict=1) + + if not gateway_account: + frappe.throw(_("Payment Gateway Account is not configured")) + + return gateway_account + +@frappe.whitelist() +def get_print_format_list(ref_doctype): + print_format_list = ["Standard"] + + print_format_list.extend([p.name for p in frappe.get_all("Print Format", + filters={"doc_type": ref_doctype})]) + + return { + "print_format": print_format_list + } + +@frappe.whitelist(allow_guest=True) +def generate_payment_request(name): + payment_url = frappe.get_doc("Payment Request", name).run_method("get_payment_url") + if payment_url: + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = payment_url + +@frappe.whitelist(allow_guest=True) +def resend_payment_email(docname): + return frappe.get_doc("Payment Request", docname).send_email() + \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js new file mode 100644 index 0000000000..0caf1c2f7f --- /dev/null +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -0,0 +1,17 @@ +frappe.listview_settings['Payment Request'] = { + add_fields: ["status"], + get_indicator: function(doc) { + if(doc.status == "Draft") { + return [__("Draft"), "darkgrey", "status,=,Draft"]; + } + else if(doc.status == "Initiated") { + return [__("Initiated"), "green", "status,=,Initiated"]; + } + else if(doc.status == "Paid") { + return [__("Paid"), "blue", "status,=,Paid"]; + } + else if(doc.status == "Cancelled") { + return [__("Cancelled"), "orange", "status,=,Cancelled"]; + } + } +} diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py new file mode 100644 index 0000000000..a1e975a984 --- /dev/null +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -0,0 +1,79 @@ +# -*- 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 erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +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 +# test_records = frappe.get_test_records('Payment Request') + +test_dependencies = ["Currency Exchange", "Journal Entry", "Contact", "Address"] + +payment_gateway = { + "doctype": "Payment Gateway", + "gateway": "_Test Gateway" +} + +payment_method = [ + { + "doctype": "Payment Gateway Account", + "is_default": 1, + "gateway": "_Test Gateway", + "payment_account": "_Test Bank - _TC", + "currency": "INR" + }, + { + "doctype": "Payment Gateway Account", + "gateway": "_Test Gateway", + "payment_account": "_Test Bank - _TC", + "currency": "USD" + } +] + +class TestPaymentRequest(unittest.TestCase): + def setUp(self): + if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"): + frappe.get_doc(payment_gateway).insert(ignore_permissions=True) + + for method in payment_method: + if not frappe.db.get_value("Payment Gateway Account", {"gateway": method["gateway"], + "currency": method["currency"]}, "name"): + frappe.get_doc(method).insert(ignore_permissions=True) + + def test_payment_request_linkings(self): + SO_INR = make_sales_order(currency="INR") + pr = make_payment_request(dt="Sales Order", dn=SO_INR.name, recipient_id="saurabh@erpnext.com") + + self.assertEquals(pr.reference_doctype, "Sales Order") + self.assertEquals(pr.reference_name, SO_INR.name) + self.assertEquals(pr.currency, "INR") + + SI_USD = create_sales_invoice(currency="USD", conversion_rate=50) + pr = make_payment_request(dt="Sales Invoice", dn=SI_USD.name, recipient_id="saurabh@erpnext.com") + + self.assertEquals(pr.reference_doctype, "Sales Invoice") + self.assertEquals(pr.reference_name, SI_USD.name) + self.assertEquals(pr.currency, "USD") + + def test_payment_entry(self): + 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() + + SO_INR = frappe.get_doc("Sales Order", SO_INR.name) + + self.assertEquals(SO_INR.advance_paid, jv.total_debit) + + SI_USD = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + 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, payemnt_gateway="_Test Gateway - USD") + + self.assertRaises(frappe.ValidationError, pr.save) + + diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 1b541cf063..6b03afa3ee 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -75,6 +75,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte if(doc.outstanding_amount!=0 && !cint(doc.is_return)) { cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry).addClass("btn-primary"); + cur_frm.add_custom_button(__('Make Payment Request'), this.make_payment_request); } } diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index e7f2b8ab0e..6f02a54742 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -247,7 +247,7 @@ def update_against_doc(d, jv_obj): # will work as update after submit jv_obj.flags.ignore_validate_update_after_submit = True - jv_obj.save() + jv_obj.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` diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index 8a27394509..859764d301 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -32,6 +32,11 @@ def get_data(): "name": "Supplier", "description": _("Supplier database.") }, + { + "type": "doctype", + "name": "Payment Request", + "description": _("Payment Request") + }, { "type": "page", "name": "Accounts Browser", @@ -83,6 +88,11 @@ def get_data(): "name": "Fiscal Year", "description": _("Financial / accounting year.") }, + { + "type": "doctype", + "name": "Payment Gateway Account", + "description": _("Setup Gateway accounts.") + }, { "type": "page", "name": "Accounts Browser", diff --git a/erpnext/docs/assets/img/accounts/pr-details-1.png b/erpnext/docs/assets/img/accounts/pr-details-1.png new file mode 100644 index 0000000000..12e40a6a89 Binary files /dev/null and b/erpnext/docs/assets/img/accounts/pr-details-1.png differ diff --git a/erpnext/docs/assets/img/accounts/pr-details-2.png b/erpnext/docs/assets/img/accounts/pr-details-2.png new file mode 100644 index 0000000000..43122e30c8 Binary files /dev/null and b/erpnext/docs/assets/img/accounts/pr-details-2.png differ diff --git a/erpnext/docs/assets/img/accounts/pr-email.png b/erpnext/docs/assets/img/accounts/pr-email.png new file mode 100644 index 0000000000..de1a86890f Binary files /dev/null and b/erpnext/docs/assets/img/accounts/pr-email.png differ diff --git a/erpnext/docs/assets/img/accounts/pr-from-si.png b/erpnext/docs/assets/img/accounts/pr-from-si.png new file mode 100644 index 0000000000..c9e654ae0e Binary files /dev/null and b/erpnext/docs/assets/img/accounts/pr-from-si.png differ diff --git a/erpnext/docs/assets/img/accounts/pr-from-so.png b/erpnext/docs/assets/img/accounts/pr-from-so.png new file mode 100644 index 0000000000..b41ea4fd75 Binary files /dev/null and b/erpnext/docs/assets/img/accounts/pr-from-so.png differ diff --git a/erpnext/docs/user/manual/en/accounts/index.txt b/erpnext/docs/user/manual/en/accounts/index.txt index 391a1852b7..5f1b5d5417 100644 --- a/erpnext/docs/user/manual/en/accounts/index.txt +++ b/erpnext/docs/user/manual/en/accounts/index.txt @@ -1,6 +1,7 @@ journal-entry sales-invoice purchase-invoice +payment-request chart-of-accounts making-payments advance-payment-entry diff --git a/erpnext/docs/user/manual/en/accounts/pyament-request.md b/erpnext/docs/user/manual/en/accounts/pyament-request.md new file mode 100644 index 0000000000..ca696c648c --- /dev/null +++ b/erpnext/docs/user/manual/en/accounts/pyament-request.md @@ -0,0 +1,28 @@ +Payment Request will act as mediator between Payment Gateway and ERPNext. You can create payment request via Sales Order or Sales Invoice. + +- Create Payment Request via Sales Order +Payment Request + +- Create payment Request via Sales Invoice +Payment Request + +--- + +Select appropriate Payment Gateway Account on Payment Request. Account head specified on payment gateway will +considered to create journal entry. + +Note: Invoice/Order Currency and Payment Gateway Account corruncy should be same. + +Payment Request + +--- + +##### Notify Customer +You can notify customer from Payment Request with print format. If customer contact email is mentioned, it will automatically fetch email. If not so you can set email id on Payment Request. + +Payment Request + +##### Request Mail +Payment Request + + diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js index d437d74e96..b667177157 100644 --- a/erpnext/public/js/shopping_cart.js +++ b/erpnext/public/js/shopping_cart.js @@ -4,13 +4,12 @@ // shopping cart frappe.provide("shopping_cart"); -$(function() { +frappe.ready(function() { // update user if(full_name) { $('.navbar li[data-label="User"] a') .html(' ' + full_name); } - // update login shopping_cart.set_cart_count(); }); @@ -33,10 +32,9 @@ $.extend(shopping_cart, { }, btn: opts.btn, callback: function(r) { + shopping_cart.set_cart_count(); if(opts.callback) opts.callback(r); - - shopping_cart.set_cart_count(); } }); } @@ -44,13 +42,29 @@ $.extend(shopping_cart, { set_cart_count: function() { var cart_count = getCookie("cart_count"); - var $cart = $('.dropdown [data-label="Cart"]'); - var $badge = $cart.find(".badge"); + + if($(".cart-icon").length == 0) { + $('
\ + \ +
Cart\ + \ + \ +
\ +
').appendTo($('.hidden-xs')) + } + + var $cart = $('.cart-icon'); + var $badge = $cart.find("#cart-count"); + + if(parseInt(cart_count) === 0 || cart_count === undefined) { + $cart.css("display", "none"); + } + else { + $cart.css("display", "inline"); + } + if(cart_count) { - if($badge.length === 0) { - var $badge = $('') - .prependTo($cart.find("a").addClass("badge-hover")); - } $badge.html(cart_count); } else { $badge.remove(); diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 56c8da0578..74b52027c5 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -41,45 +41,47 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( } } - // material request - if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 - && flt(doc.per_delivered, 2) < 100) { - cur_frm.add_custom_button(__('Material Request'), this.make_material_request); - } - - // make purchase order - if(flt(doc.per_delivered, 2) < 100 && allow_purchase) { - cur_frm.add_custom_button(__('Purchase Order'), cur_frm.cscript.make_purchase_order); - } - - if(flt(doc.per_billed)==0) { - cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry); - } - if (this.frm.has_perm("submit")) { // stop if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) { - cur_frm.add_custom_button(__('Stop'), this.stop_sales_order) + cur_frm.add_custom_button(__('Stop'), this.stop_sales_order, __("Status")) } - cur_frm.add_custom_button(__('Close'), this.close_sales_order) - } - - // maintenance - if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) { - cur_frm.add_custom_button(__('Maint. Visit'), this.make_maintenance_visit); - cur_frm.add_custom_button(__('Maint. Schedule'), this.make_maintenance_schedule); + cur_frm.add_custom_button(__('Close'), this.close_sales_order, __("Status")) } // delivery note if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) { - cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary"); + cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note, __("Make")); + // cur_frm.page.set_inner_btn_group_as_primary(__("Make")); } // sales invoice if(flt(doc.per_billed, 2) < 100) { - cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary"); + cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice, __("Make")); + } + + // material request + if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 + && flt(doc.per_delivered, 2) < 100) { + cur_frm.add_custom_button(__('Material Request'), this.make_material_request, __("Make")); + } + + // make purchase order + if(flt(doc.per_delivered, 2) < 100 && allow_purchase) { + cur_frm.add_custom_button(__('Purchase Order'), cur_frm.cscript.make_purchase_order, __("Make")); + } + + 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")); + } + + // maintenance + 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")); } diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index db8cb8121d..ee97334ad2 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -2802,7 +2802,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2015-12-17 16:19:40.324514", + "modified": "2015-12-29 12:32:45.649349", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 99e708d5b0..8bc96e666b 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -288,11 +288,26 @@ class SalesOrder(SellingController): frappe.db.set_value("Sales Order", self.name, "per_delivered", flt(delivered_qty/tot_qty) * 100, update_modified=False) + + def set_indicator(self): + """Set indicator for portal""" + if self.per_billed < 100 and self.per_delivered < 100: + self.indicator_color = "orange" + self.indicator_title = _("Not Paid and Not Delivered") + + elif self.per_billed == 100 and self.per_delivered < 100: + self.indicator_color = "orange" + self.indicator_title = _("Paid and Not Delivered") + else: + self.indicator_color = "green" + self.indicator_title = _("Paid") + 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["title"] = _("My Orders") + list_context["parents"] = [{"title": _("My Account"), "name": "me"}] return list_context @frappe.whitelist() @@ -401,7 +416,7 @@ def make_delivery_note(source_name, target_doc=None): return target_doc @frappe.whitelist() -def make_sales_invoice(source_name, target_doc=None): +def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): def postprocess(source, target): set_missing_values(source, target) #Get the advance paid Journal Entries in Sales Invoice Advance @@ -410,6 +425,7 @@ def make_sales_invoice(source_name, target_doc=None): def set_missing_values(source, target): target.is_pos = 0 target.ignore_pricing_rule = 1 + target.flags.ignore_permissions = True target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") @@ -442,7 +458,7 @@ def make_sales_invoice(source_name, target_doc=None): "doctype": "Sales Team", "add_if_empty": True } - }, target_doc, postprocess) + }, target_doc, postprocess, ignore_permissions=ignore_permissions) return doclist diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 6a8744a385..7f17bd3837 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -288,6 +288,24 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ } } refresh_field('product_bundle_help'); + }, + + make_payment_request: function() { + frappe.call({ + method:"erpnext.accounts.doctype.payment_request.payment_request.make_payment_request", + args: { + "dt": cur_frm.doc.doctype, + "dn": cur_frm.doc.name, + "recipient_id": cur_frm.doc.contact_email + }, + callback: function(r) { + if(!r.exc){ + var doc = frappe.model.sync(r.message); + console.log(r.message) + frappe.set_route("Form", r.message.doctype, r.message.name); + } + } + }) } }); diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py index 9be2880eb0..081afcc7c3 100644 --- a/erpnext/setup/setup_wizard/setup_wizard.py +++ b/erpnext/setup/setup_wizard/setup_wizard.py @@ -124,7 +124,7 @@ def create_bank_account(args): "account_type": "Bank", }) try: - bank_account.insert() + return bank_account.insert() except RootNotEditable: frappe.throw(_("Bank account cannot be named as {0}").format(args.get("bank_account"))) diff --git a/erpnext/shopping_cart/utils.py b/erpnext/shopping_cart/utils.py index 7794a8fb9f..b8d5054e87 100644 --- a/erpnext/shopping_cart/utils.py +++ b/erpnext/shopping_cart/utils.py @@ -28,17 +28,7 @@ def update_website_context(context): cart_enabled = is_cart_enabled() context["shopping_cart_enabled"] = cart_enabled - if cart_enabled: - post_login = [ - {"label": _("Cart"), "url": "cart", "class": "cart-count"}, - {"class": "divider"} - ] - context["post_login"] = post_login + context.get("post_login", []) - def update_my_account_context(context): - if is_cart_enabled(): - context["my_account_list"].append({"label": _("Cart"), "url": "cart"}) - context["my_account_list"].extend([ {"label": _("Orders"), "url": "orders"}, {"label": _("Invoices"), "url": "invoices"}, diff --git a/erpnext/templates/generators/item.html b/erpnext/templates/generators/item.html index dc48e0df12..34d345fd2f 100644 --- a/erpnext/templates/generators/item.html +++ b/erpnext/templates/generators/item.html @@ -71,18 +71,7 @@ style="display: none; padding-left: 0px; padding-right: 0px; padding-top: 10px;"> -
- -
-
- -
-
- - {{ _("View Cart") }} -
+ {{ _("Goto Cart") }} diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js index e413f0b2e9..0c29569b9e 100644 --- a/erpnext/templates/includes/cart.js +++ b/erpnext/templates/includes/cart.js @@ -65,11 +65,11 @@ $.extend(shopping_cart, { if(!r.exc) { $(".cart-items").html(r.message.items); $(".cart-tax-items").html(r.message.taxes); + $(".cart-icon").hide(); } }, }); }); - }, render_tax_row: function($cart_taxes, doc, shipping_rules) { @@ -144,5 +144,6 @@ $.extend(shopping_cart, { }); $(document).ready(function() { + $(".cart-icon").hide(); shopping_cart.bind_events(); }); diff --git a/erpnext/templates/includes/product_page.js b/erpnext/templates/includes/product_page.js index 0a38c23f9b..f4d90279c5 100644 --- a/erpnext/templates/includes/product_page.js +++ b/erpnext/templates/includes/product_page.js @@ -49,21 +49,6 @@ frappe.ready(function() { }); }); - $("#item-update-cart button").on("click", function() { - shopping_cart.update_cart({ - item_code: get_item_code(), - qty: $("#item-update-cart input").val(), - btn: this, - callback: function(r) { - if(r.exc) { - $("#item-update-cart input").val(qty); - } else { - qty = $("#item-update-cart input").val(); - } - }, - }); - }); - $("[itemscope] .item-view-attribute .form-control").on("change", function() { try { var item_code = encodeURIComponent(get_item_code()); diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html index 26dbed6a3e..3bd6ad30d0 100644 --- a/erpnext/templates/pages/order.html +++ b/erpnext/templates/pages/order.html @@ -5,6 +5,10 @@ {% endblock %} +{% block breadcrumbs %} + {% include "templates/includes/breadcrumbs.html" %} +{% endblock %} + {% block style %}