Payment Tool #2106
This commit is contained in:
parent
84e08fd534
commit
8e7ca41817
@ -25,7 +25,8 @@ class GLEntry(Document):
|
|||||||
validate_balance_type(self.account, adv_adj)
|
validate_balance_type(self.account, adv_adj)
|
||||||
|
|
||||||
# Update outstanding amt on against voucher
|
# Update outstanding amt on against voucher
|
||||||
if self.against_voucher and update_outstanding == 'Yes':
|
if self.against_voucher_type in ['Journal Voucher', 'Sales Invoice', 'Purchase Invoice'] \
|
||||||
|
and self.against_voucher and update_outstanding == 'Yes':
|
||||||
update_outstanding_amt(self.account, self.against_voucher_type,
|
update_outstanding_amt(self.account, self.against_voucher_type,
|
||||||
self.against_voucher)
|
self.against_voucher)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils import cint, cstr, flt, fmt_money, formatdate, getdate
|
from frappe.utils import cint, cstr, flt, fmt_money, formatdate, getdate
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _, scrub
|
||||||
from erpnext.setup.utils import get_company_currency
|
from erpnext.setup.utils import get_company_currency
|
||||||
|
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
@ -35,18 +35,35 @@ class JournalVoucher(AccountsController):
|
|||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_aging_date()
|
self.set_aging_date()
|
||||||
self.set_print_format_fields()
|
self.set_print_format_fields()
|
||||||
|
self.validate_against_sales_order()
|
||||||
|
self.validate_against_purchase_order()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if self.voucher_type in ['Bank Voucher', 'Contra Voucher', 'Journal Entry']:
|
if self.voucher_type in ['Bank Voucher', 'Contra Voucher', 'Journal Entry']:
|
||||||
self.check_credit_days()
|
self.check_credit_days()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
self.check_credit_limit()
|
self.check_credit_limit()
|
||||||
|
self.update_advance_paid()
|
||||||
|
|
||||||
|
def update_advance_paid(self):
|
||||||
|
advance_paid = frappe._dict()
|
||||||
|
for d in self.get("entries"):
|
||||||
|
if d.is_advance:
|
||||||
|
if d.against_sales_order:
|
||||||
|
advance_paid.setdefault("Sales Order", []).append(d.against_sales_order)
|
||||||
|
elif d.against_purchase_order:
|
||||||
|
advance_paid.setdefault("Purchase Order", []).append(d.against_purchase_order)
|
||||||
|
|
||||||
|
for voucher_type, order_list in advance_paid.items():
|
||||||
|
for voucher_no in list(set(order_list)):
|
||||||
|
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||||
remove_against_link_from_jv(self.doctype, self.name, "against_jv")
|
remove_against_link_from_jv(self.doctype, self.name, "against_jv")
|
||||||
|
|
||||||
self.make_gl_entries(1)
|
self.make_gl_entries(1)
|
||||||
|
self.update_advance_paid()
|
||||||
|
|
||||||
def validate_cheque_info(self):
|
def validate_cheque_info(self):
|
||||||
if self.voucher_type in ['Bank Voucher']:
|
if self.voucher_type in ['Bank Voucher']:
|
||||||
@ -64,7 +81,8 @@ class JournalVoucher(AccountsController):
|
|||||||
master_type = frappe.db.get_value("Account", d.account, "master_type")
|
master_type = frappe.db.get_value("Account", d.account, "master_type")
|
||||||
if (master_type == 'Customer' and flt(d.credit) > 0) or \
|
if (master_type == 'Customer' and flt(d.credit) > 0) or \
|
||||||
(master_type == 'Supplier' and flt(d.debit) > 0):
|
(master_type == 'Supplier' and flt(d.debit) > 0):
|
||||||
msgprint(_("Please check 'Is Advance' against Account {0} if this is an advance entry.").format(d.account))
|
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this \
|
||||||
|
is an advance entry.").format(d.idx, d.account))
|
||||||
|
|
||||||
def validate_against_jv(self):
|
def validate_against_jv(self):
|
||||||
for d in self.get('entries'):
|
for d in self.get('entries'):
|
||||||
@ -90,24 +108,86 @@ class JournalVoucher(AccountsController):
|
|||||||
.format(d.against_jv, dr_or_cr))
|
.format(d.against_jv, dr_or_cr))
|
||||||
|
|
||||||
def validate_against_sales_invoice(self):
|
def validate_against_sales_invoice(self):
|
||||||
for d in self.get("entries"):
|
payment_against_voucher = self.validate_account_in_against_voucher("against_invoice", "Sales Invoice")
|
||||||
if d.against_invoice:
|
self.validate_against_invoice_fields("Sales Invoice", payment_against_voucher)
|
||||||
if d.debit > 0:
|
|
||||||
frappe.throw(_("Row {0}: Debit entry can not be linked with a Sales Invoice")
|
|
||||||
.format(d.idx))
|
|
||||||
if frappe.db.get_value("Sales Invoice", d.against_invoice, "debit_to") != d.account:
|
|
||||||
frappe.throw(_("Row {0}: Account does not match with \
|
|
||||||
Sales Invoice Debit To account").format(d.idx, d.account))
|
|
||||||
|
|
||||||
def validate_against_purchase_invoice(self):
|
def validate_against_purchase_invoice(self):
|
||||||
|
payment_against_voucher = self.validate_account_in_against_voucher("against_voucher", "Purchase Invoice")
|
||||||
|
self.validate_against_invoice_fields("Purchase Invoice", payment_against_voucher)
|
||||||
|
|
||||||
|
def validate_against_sales_order(self):
|
||||||
|
payment_against_voucher = self.validate_account_in_against_voucher("against_sales_order", "Sales Order")
|
||||||
|
self.validate_against_order_fields("Sales Order", payment_against_voucher)
|
||||||
|
|
||||||
|
def validate_against_purchase_order(self):
|
||||||
|
payment_against_voucher = self.validate_account_in_against_voucher("against_purchase_order", "Purchase Order")
|
||||||
|
self.validate_against_order_fields("Purchase Order", payment_against_voucher)
|
||||||
|
|
||||||
|
def validate_account_in_against_voucher(self, against_field, doctype):
|
||||||
|
payment_against_voucher = frappe._dict()
|
||||||
|
field_dict = {'Sales Invoice': "Debit To",
|
||||||
|
'Purchase Invoice': "Credit To",
|
||||||
|
'Sales Order': "Customer",
|
||||||
|
'Purchase Order': "Supplier"
|
||||||
|
}
|
||||||
|
|
||||||
for d in self.get("entries"):
|
for d in self.get("entries"):
|
||||||
if d.against_voucher:
|
if d.get(against_field):
|
||||||
if flt(d.credit) > 0:
|
dr_or_cr = "credit" if against_field in ["against_invoice", "against_sales_order"] \
|
||||||
frappe.throw(_("Row {0}: Credit entry can not be linked with a Purchase Invoice")
|
else "debit"
|
||||||
.format(d.idx))
|
if against_field in ["against_invoice", "against_sales_order"] \
|
||||||
if frappe.db.get_value("Purchase Invoice", d.against_voucher, "credit_to") != d.account:
|
and flt(d.debit) > 0:
|
||||||
frappe.throw(_("Row {0}: Account does not match with \
|
frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, doctype))
|
||||||
Purchase Invoice Credit To account").format(d.idx, d.account))
|
|
||||||
|
if against_field in ["against_voucher", "against_purchase_order"] \
|
||||||
|
and flt(d.credit) > 0:
|
||||||
|
frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, doctype))
|
||||||
|
|
||||||
|
voucher_account = frappe.db.get_value(doctype, d.get(against_field), \
|
||||||
|
scrub(field_dict.get(doctype)))
|
||||||
|
|
||||||
|
account_master_name = frappe.db.get_value("Account", d.account, "master_name")
|
||||||
|
|
||||||
|
if against_field in ["against_invoice", "against_voucher"] \
|
||||||
|
and voucher_account != d.account:
|
||||||
|
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
||||||
|
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
||||||
|
|
||||||
|
if against_field in ["against_sales_order", "against_purchase_order"]:
|
||||||
|
if voucher_account != account_master_name:
|
||||||
|
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} Name") \
|
||||||
|
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
||||||
|
elif d.is_advance == "Yes":
|
||||||
|
payment_against_voucher.setdefault(d.get(against_field), []).append(flt(d.get(dr_or_cr)))
|
||||||
|
|
||||||
|
return payment_against_voucher
|
||||||
|
|
||||||
|
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
||||||
|
for voucher_no, payment_list in payment_against_voucher.items():
|
||||||
|
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||||
|
["docstatus", "outstanding_amount"])
|
||||||
|
|
||||||
|
if voucher_properties[0] != 1:
|
||||||
|
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||||
|
|
||||||
|
if flt(voucher_properties[1]) < flt(sum(payment_list)):
|
||||||
|
frappe.throw(_("Payment against {0} {1} cannot be greater \
|
||||||
|
than Outstanding Amount {2}").format(doctype, voucher_no, voucher_properties[1]))
|
||||||
|
|
||||||
|
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
||||||
|
for voucher_no, payment_list in payment_against_voucher.items():
|
||||||
|
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||||
|
["docstatus", "per_billed", "advance_paid", "grand_total"])
|
||||||
|
|
||||||
|
if voucher_properties[0] != 1:
|
||||||
|
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||||
|
|
||||||
|
if flt(voucher_properties[1]) >= 100:
|
||||||
|
frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no))
|
||||||
|
|
||||||
|
if flt(voucher_properties[3]) < flt(voucher_properties[2]) + flt(sum(payment_list)):
|
||||||
|
frappe.throw(_("Advance paid against {0} {1} cannot be greater \
|
||||||
|
than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3]))
|
||||||
|
|
||||||
def set_against_account(self):
|
def set_against_account(self):
|
||||||
accounts_debited, accounts_credited = [], []
|
accounts_debited, accounts_credited = [], []
|
||||||
@ -147,7 +227,13 @@ class JournalVoucher(AccountsController):
|
|||||||
for d in self.get('entries'):
|
for d in self.get('entries'):
|
||||||
if d.against_invoice and d.credit:
|
if d.against_invoice and d.credit:
|
||||||
currency = frappe.db.get_value("Sales Invoice", d.against_invoice, "currency")
|
currency = frappe.db.get_value("Sales Invoice", d.against_invoice, "currency")
|
||||||
r.append(_("{0} {1} against Invoice {2}").format(currency, fmt_money(flt(d.credit)), d.against_invoice))
|
r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = currency), \
|
||||||
|
d.against_invoice))
|
||||||
|
|
||||||
|
if d.against_sales_order and d.credit:
|
||||||
|
currency = frappe.db.get_value("Sales Order", d.against_sales_order, "currency")
|
||||||
|
r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = currency), \
|
||||||
|
d.against_sales_order))
|
||||||
|
|
||||||
if d.against_voucher and d.debit:
|
if d.against_voucher and d.debit:
|
||||||
bill_no = frappe.db.sql("""select bill_no, bill_date, currency
|
bill_no = frappe.db.sql("""select bill_no, bill_date, currency
|
||||||
@ -158,13 +244,17 @@ class JournalVoucher(AccountsController):
|
|||||||
fmt_money(flt(d.debit)), bill_no[0][0],
|
fmt_money(flt(d.debit)), bill_no[0][0],
|
||||||
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
|
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
|
||||||
|
|
||||||
|
if d.against_purchase_order and d.debit:
|
||||||
|
currency = frappe.db.get_value("Purchase Order", d.against_purchase_order, "currency")
|
||||||
|
r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = currency), \
|
||||||
|
d.against_purchase_order))
|
||||||
|
|
||||||
if self.user_remark:
|
if self.user_remark:
|
||||||
r.append(_("Note: {0}").format(self.user_remark))
|
r.append(_("Note: {0}").format(self.user_remark))
|
||||||
|
|
||||||
if r:
|
if r:
|
||||||
self.remark = ("\n").join(r)
|
self.remark = ("\n").join(r) #User Remarks is not mandatory
|
||||||
else:
|
|
||||||
frappe.msgprint(_("User Remarks is mandatory"), raise_exception=frappe.MandatoryError)
|
|
||||||
|
|
||||||
def set_aging_date(self):
|
def set_aging_date(self):
|
||||||
if self.is_opening != 'Yes':
|
if self.is_opening != 'Yes':
|
||||||
@ -264,14 +354,18 @@ class JournalVoucher(AccountsController):
|
|||||||
"against": d.against_account,
|
"against": d.against_account,
|
||||||
"debit": flt(d.debit, self.precision("debit", "entries")),
|
"debit": flt(d.debit, self.precision("debit", "entries")),
|
||||||
"credit": flt(d.credit, self.precision("credit", "entries")),
|
"credit": flt(d.credit, self.precision("credit", "entries")),
|
||||||
"against_voucher_type": ((d.against_voucher and "Purchase Invoice")
|
"against_voucher_type": (("Purchase Invoice" if d.against_voucher else None)
|
||||||
or (d.against_invoice and "Sales Invoice")
|
or ("Sales Invoice" if d.against_invoice else None)
|
||||||
or (d.against_jv and "Journal Voucher")),
|
or ("Journal Voucher" if d.against_jv else None)
|
||||||
"against_voucher": d.against_voucher or d.against_invoice or d.against_jv,
|
or ("Sales Order" if d.against_sales_order else None)
|
||||||
|
or ("Purchase Order" if d.against_purchase_order else None)),
|
||||||
|
"against_voucher": d.against_voucher or d.against_invoice or d.against_jv
|
||||||
|
or d.against_sales_order or d.against_purchase_order,
|
||||||
"remarks": self.remark,
|
"remarks": self.remark,
|
||||||
"cost_center": d.cost_center
|
"cost_center": d.cost_center
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if gl_map:
|
if gl_map:
|
||||||
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj)
|
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj)
|
||||||
|
|
||||||
|
@ -1,41 +1,88 @@
|
|||||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import unittest
|
import unittest, frappe
|
||||||
import frappe
|
from frappe.utils import flt
|
||||||
|
|
||||||
class TestJournalVoucher(unittest.TestCase):
|
class TestJournalVoucher(unittest.TestCase):
|
||||||
def test_journal_voucher_with_against_jv(self):
|
def test_journal_voucher_with_against_jv(self):
|
||||||
self.clear_account_balance()
|
|
||||||
jv_invoice = frappe.copy_doc(test_records[2])
|
jv_invoice = frappe.copy_doc(test_records[2])
|
||||||
jv_invoice.insert()
|
base_jv = frappe.copy_doc(test_records[0])
|
||||||
jv_invoice.submit()
|
self.jv_against_voucher_testcase(base_jv, jv_invoice)
|
||||||
|
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
def test_jv_against_sales_order(self):
|
||||||
where account = %s and docstatus = 1 and parent = %s""",
|
from erpnext.selling.doctype.sales_order.test_sales_order \
|
||||||
("_Test Customer - _TC", jv_invoice.name)))
|
import test_records as so_test_records
|
||||||
|
|
||||||
|
sales_order = frappe.copy_doc(so_test_records[0])
|
||||||
|
base_jv = frappe.copy_doc(test_records[0])
|
||||||
|
self.jv_against_voucher_testcase(base_jv, sales_order)
|
||||||
|
|
||||||
|
def test_jv_against_purchase_order(self):
|
||||||
|
from erpnext.buying.doctype.purchase_order.test_purchase_order \
|
||||||
|
import test_records as po_test_records
|
||||||
|
|
||||||
|
purchase_order = frappe.copy_doc(po_test_records[0])
|
||||||
|
base_jv = frappe.copy_doc(test_records[1])
|
||||||
|
self.jv_against_voucher_testcase(base_jv, purchase_order)
|
||||||
|
|
||||||
|
def jv_against_voucher_testcase(self, base_jv, test_voucher):
|
||||||
|
dr_or_cr = "credit" if test_voucher.doctype in ["Sales Order", "Journal Voucher"] else "debit"
|
||||||
|
field_dict = {'Journal Voucher': "against_jv",
|
||||||
|
'Sales Order': "against_sales_order",
|
||||||
|
'Purchase Order': "against_purchase_order"
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clear_account_balance()
|
||||||
|
test_voucher.insert()
|
||||||
|
test_voucher.submit()
|
||||||
|
|
||||||
|
if test_voucher.doctype == "Journal Voucher":
|
||||||
|
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||||
|
where account = %s and docstatus = 1 and parent = %s""",
|
||||||
|
("_Test Customer - _TC", test_voucher.name)))
|
||||||
|
|
||||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||||
where against_jv=%s""", jv_invoice.name))
|
where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (test_voucher.name)))
|
||||||
|
|
||||||
jv_payment = frappe.copy_doc(test_records[0])
|
base_jv.get("entries")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
|
||||||
jv_payment.get("entries")[0].against_jv = jv_invoice.name
|
base_jv.get("entries")[0].set(field_dict.get(test_voucher.doctype), test_voucher.name)
|
||||||
jv_payment.insert()
|
base_jv.insert()
|
||||||
jv_payment.submit()
|
base_jv.submit()
|
||||||
|
|
||||||
|
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
|
||||||
|
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||||
where against_jv=%s""", jv_invoice.name))
|
where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (submitted_voucher.name)))
|
||||||
|
|
||||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||||
where against_jv=%s and credit=400""", jv_invoice.name))
|
where %s=%s and %s=400""" % (field_dict.get(submitted_voucher.doctype), '%s', dr_or_cr), (submitted_voucher.name)))
|
||||||
|
|
||||||
# cancel jv_invoice
|
if base_jv.get("entries")[0].is_advance == "Yes":
|
||||||
jv_invoice.cancel()
|
self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr)
|
||||||
|
self.cancel_against_voucher_testcase(submitted_voucher)
|
||||||
|
|
||||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
def advance_paid_testcase(self, base_jv, test_voucher, dr_or_cr):
|
||||||
where against_jv=%s""", jv_invoice.name))
|
#Test advance paid field
|
||||||
|
advance_paid = frappe.db.sql("""select advance_paid from `tab%s`
|
||||||
|
where name=%s""" % (test_voucher.doctype, '%s'), (test_voucher.name))
|
||||||
|
payment_against_order = base_jv.get("entries")[0].get(dr_or_cr)
|
||||||
|
|
||||||
|
self.assertTrue(flt(advance_paid[0][0]) == flt(payment_against_order))
|
||||||
|
|
||||||
|
def cancel_against_voucher_testcase(self, test_voucher):
|
||||||
|
if test_voucher.doctype == "Journal Voucher":
|
||||||
|
# if test_voucher is a Journal Voucher, test cancellation of test_voucher
|
||||||
|
test_voucher.cancel()
|
||||||
|
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||||
|
where against_jv=%s""", test_voucher.name))
|
||||||
|
|
||||||
|
elif test_voucher.doctype in ["Sales Order", "Purchase Order"]:
|
||||||
|
# if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher
|
||||||
|
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
|
||||||
|
self.assertRaises(frappe.LinkExistsError, submitted_voucher.cancel)
|
||||||
|
|
||||||
def test_jv_against_stock_account(self):
|
def test_jv_against_stock_account(self):
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||||
|
@ -117,11 +117,6 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "col_break3",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"permlevel": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "against_jv",
|
"fieldname": "against_jv",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -135,6 +130,25 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "col_break3",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"permlevel": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "against_sales_order",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Against Sales Order",
|
||||||
|
"options": "Sales Order",
|
||||||
|
"permlevel": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "against_purchase_order",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Against Purchase Order",
|
||||||
|
"options": "Purchase Order",
|
||||||
|
"permlevel": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "is_advance",
|
"fieldname": "is_advance",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@ -160,7 +174,7 @@
|
|||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2014-07-25 03:16:51.149899",
|
"modified": "2014-08-20 12:19:55.049973",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Voucher Detail",
|
"name": "Journal Voucher Detail",
|
||||||
|
215
erpnext/accounts/doctype/payment_tool/payment_tool.js
Normal file
215
erpnext/accounts/doctype/payment_tool/payment_tool.js
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.provide("erpnext.payment_tool");
|
||||||
|
|
||||||
|
// Help content
|
||||||
|
frappe.ui.form.on("Payment Tool", "onload", function(frm) {
|
||||||
|
var help_content = '<i class="icon-hand-right"></i> Note:<br>'+
|
||||||
|
'<ul>If payment is not made against any reference, make Journal Voucher manually.</ul>';
|
||||||
|
frm.set_value("make_jv_help", help_content);
|
||||||
|
|
||||||
|
frm.set_value("party_type", "Customer");
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on("Payment Tool", "company", function(frm) {
|
||||||
|
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on("Payment Tool", "received_or_paid", function(frm) {
|
||||||
|
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch bank/cash account based on payment mode
|
||||||
|
cur_frm.add_fetch("payment_mode", "default_account", "payment_account");
|
||||||
|
|
||||||
|
// Set party account name
|
||||||
|
frappe.ui.form.on("Payment Tool", "customer", function(frm) {
|
||||||
|
erpnext.payment_tool.set_party_account(frm);
|
||||||
|
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on("Payment Tool", "supplier", function(frm) {
|
||||||
|
erpnext.payment_tool.set_party_account(frm);
|
||||||
|
erpnext.payment_tool.check_mandatory_to_set_button(frm);
|
||||||
|
});
|
||||||
|
|
||||||
|
erpnext.payment_tool.check_mandatory_to_set_button = function(frm) {
|
||||||
|
if (frm.doc.company && frm.doc.party_type && frm.doc.received_or_paid && (frm.doc.customer || frm.doc.supplier)) {
|
||||||
|
frm.fields_dict.get_outstanding_vouchers.$input.addClass("btn-primary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set Button color
|
||||||
|
erpnext.payment_tool.set_party_account = function(frm) {
|
||||||
|
if(frm.doc.party_type == "Customer") {
|
||||||
|
var party_name = frm.doc.customer;
|
||||||
|
} else {
|
||||||
|
var party_name = frm.doc.supplier;
|
||||||
|
}
|
||||||
|
return frappe.call({
|
||||||
|
method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_party_account',
|
||||||
|
args: {
|
||||||
|
party_type: frm.doc.party_type,
|
||||||
|
party_name: party_name
|
||||||
|
},
|
||||||
|
callback: function(r, rt) {
|
||||||
|
if(!r.exc) {
|
||||||
|
frm.set_value("party_account", r.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get outstanding vouchers
|
||||||
|
frappe.ui.form.on("Payment Tool", "get_outstanding_vouchers", function(frm) {
|
||||||
|
erpnext.payment_tool.check_mandatory_to_fetch(frm.doc);
|
||||||
|
|
||||||
|
frm.set_value("payment_tool_details", []);
|
||||||
|
|
||||||
|
return frappe.call({
|
||||||
|
method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_outstanding_vouchers',
|
||||||
|
args: {
|
||||||
|
args: {
|
||||||
|
"company": frm.doc.company,
|
||||||
|
"party_type": frm.doc.party_type,
|
||||||
|
"received_or_paid": frm.doc.received_or_paid,
|
||||||
|
"party_name": frm.doc.party_type == "Customer" ? frm.doc.customer : frm.doc.supplier,
|
||||||
|
"party_account": frm.doc.party_account
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback: function(r, rt) {
|
||||||
|
frm.fields_dict.get_outstanding_vouchers.$input.removeClass("btn-primary");
|
||||||
|
frm.fields_dict.make_journal_voucher.$input.addClass("btn-primary");
|
||||||
|
if(r.message) {
|
||||||
|
$.each(r.message, function(i, d) {
|
||||||
|
var invoice_detail = frappe.model.add_child(frm.doc, "Payment Tool Detail", "payment_tool_details");
|
||||||
|
invoice_detail.against_voucher_type = d.voucher_type;
|
||||||
|
invoice_detail.against_voucher_no = d.voucher_no;
|
||||||
|
invoice_detail.total_amount = d.invoice_amount;
|
||||||
|
invoice_detail.outstanding_amount = d.outstanding_amount;
|
||||||
|
});
|
||||||
|
refresh_field("payment_tool_details");
|
||||||
|
frm.refresh_dependency();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// validate against_voucher_type
|
||||||
|
frappe.ui.form.on("Payment Tool Detail", "against_voucher_type", function(frm) {
|
||||||
|
erpnext.payment_tool.validate_against_voucher(frm);
|
||||||
|
});
|
||||||
|
|
||||||
|
erpnext.payment_tool.validate_against_voucher = function(frm) {
|
||||||
|
$.each(frm.doc.payment_tool_details || [], function(i, row) {
|
||||||
|
if(frm.doc.party_type=="Customer"
|
||||||
|
&& !in_list(["Sales Order", "Sales Invoice", "Journal Voucher"], row.against_voucher_type)) {
|
||||||
|
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
|
||||||
|
frappe.throw(__("Against Voucher Type must be one of Sales Order, Sales Invoice or Journal Voucher"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frm.doc.party_type=="Supplier"
|
||||||
|
&& !in_list(["Purchase Order", "Purchase Invoice", "Journal Voucher"], row.against_voucher_type)) {
|
||||||
|
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
|
||||||
|
frappe.throw(__("Against Voucher Type must be one of Purchase Order, Purchase Invoice or Journal Voucher"))
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate against_voucher_type
|
||||||
|
frappe.ui.form.on("Payment Tool Detail", "against_voucher_no", function(frm, cdt, cdn) {
|
||||||
|
var row = locals[cdt][cdn];
|
||||||
|
frappe.call({
|
||||||
|
method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_against_voucher_amount',
|
||||||
|
args: {
|
||||||
|
"against_voucher_type": row.against_voucher_type,
|
||||||
|
"against_voucher_no": row.against_voucher_no
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if(!r.exc) {
|
||||||
|
$.each(r.message, function(k, v) {
|
||||||
|
frappe.model.set_value(cdt, cdn, k, v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set total payment amount
|
||||||
|
frappe.ui.form.on("Payment Tool Detail", "payment_amount", function(frm) {
|
||||||
|
erpnext.payment_tool.set_total_payment_amount(frm);
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on("Payment Tool Detail", "payment_tool_details_remove", function(frm) {
|
||||||
|
erpnext.payment_tool.set_total_payment_amount(frm);
|
||||||
|
});
|
||||||
|
|
||||||
|
erpnext.payment_tool.set_total_payment_amount = function(frm) {
|
||||||
|
var total_amount = 0.00;
|
||||||
|
$.each(frm.doc.payment_tool_details || [], function(i, row) {
|
||||||
|
if (row.payment_amount && (row.payment_amount <= row.outstanding_amount)) {
|
||||||
|
total_amount = total_amount + row.payment_amount;
|
||||||
|
} else {
|
||||||
|
if(row.payment_amount < 0)
|
||||||
|
msgprint(__("Row {0}: Payment amount can not be negative", [row.idx]));
|
||||||
|
else if(row.payment_amount >= row.outstanding_amount)
|
||||||
|
msgprint(__("Row {0}: Payment Amount cannot be greater than Outstanding Amount", [__(row.idx)]));
|
||||||
|
|
||||||
|
frappe.model.set_value(row.doctype, row.name, "payment_amount", 0.0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
frm.set_value("total_payment_amount", total_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Make Journal voucher
|
||||||
|
frappe.ui.form.on("Payment Tool", "make_journal_voucher", function(frm) {
|
||||||
|
erpnext.payment_tool.check_mandatory_to_fetch(frm.doc);
|
||||||
|
|
||||||
|
return frappe.call({
|
||||||
|
method: 'make_journal_voucher',
|
||||||
|
doc: frm.doc,
|
||||||
|
callback: function(r) {
|
||||||
|
frm.fields_dict.make_journal_voucher.$input.addClass("btn-primary");
|
||||||
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
cur_frm.fields_dict['payment_tool_details'].grid.get_field('against_voucher_no').get_query = function(doc, cdt, cdn) {
|
||||||
|
var c = locals[cdt][cdn];
|
||||||
|
|
||||||
|
erpnext.payment_tool.check_mandatory_to_fetch(doc);
|
||||||
|
|
||||||
|
args = { "docstatus": 1 };
|
||||||
|
|
||||||
|
if (c.against_voucher_type) {
|
||||||
|
if (in_list(["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"], c.against_voucher_type)) {
|
||||||
|
var party_type = doc.party_type.toLowerCase();
|
||||||
|
args[party_type] = doc[party_type];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
doctype: c.against_voucher_type,
|
||||||
|
filters: args
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
frappe.throw(__("Row {0}: Please specify the Against Voucher Type", [c.idx]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
erpnext.payment_tool.check_mandatory_to_fetch = function(doc) {
|
||||||
|
var check_fields = [
|
||||||
|
['Company', doc.company],
|
||||||
|
['Party Type', doc.party_type],
|
||||||
|
['Received Or Paid', doc.received_or_paid],
|
||||||
|
['Customer / Supplier', doc.party_type == "Customer" ? doc.customer : doc.supplier]
|
||||||
|
];
|
||||||
|
|
||||||
|
$.each(check_fields, function(i, v) {
|
||||||
|
if(!v[1]) frappe.throw(__("Please select {0} first", [v[0]]));
|
||||||
|
});
|
||||||
|
}
|
371
erpnext/accounts/doctype/payment_tool/payment_tool.json
Normal file
371
erpnext/accounts/doctype/payment_tool/payment_tool.json
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
{
|
||||||
|
"allow_attach": 0,
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"creation": "2014-07-23 15:12:27.746665",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "sec_break1",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Party Details",
|
||||||
|
"permlevel": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"permlevel": 0,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "party_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Party Type",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Customer\nSupplier",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"depends_on": "eval:(doc.party_type == 'Customer')",
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Customer",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Customer",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"depends_on": "eval:(doc.party_type == 'Supplier')",
|
||||||
|
"fieldname": "supplier",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Supplier",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Supplier",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "party_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Party Account",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "received_or_paid",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Received Or Paid",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Received\nPaid",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "col_break1",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Column Break 1",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "payment_mode",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Payment Mode",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Mode of Payment",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "payment_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Payment Account",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "reference_no",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Reference No",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "reference_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Reference Date",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "sec_break2",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"permlevel": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_14",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"permlevel": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "get_outstanding_vouchers",
|
||||||
|
"fieldtype": "Button",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Get Outstanding Vouchers",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"depends_on": "eval:(doc.company && doc.party_type && doc.received_or_paid && (doc.customer || doc.supplier))",
|
||||||
|
"fieldname": "sec_break3",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Against Voucher",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "payment_tool_details",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Payment Tool Details",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Payment Tool Detail",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_payment_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total Payment Amount",
|
||||||
|
"permlevel": 0,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "make_journal_voucher",
|
||||||
|
"fieldtype": "Button",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Make Journal Voucher",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "make_jv_help",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 1,
|
||||||
|
"icon": "icon-magic",
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 1,
|
||||||
|
"istable": 0,
|
||||||
|
"modified": "2014-09-05 11:15:55.484916",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Payment Tool",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 0,
|
||||||
|
"email": 0,
|
||||||
|
"export": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 0,
|
||||||
|
"read": 1,
|
||||||
|
"report": 0,
|
||||||
|
"role": "Accounts Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 0,
|
||||||
|
"email": 0,
|
||||||
|
"export": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 0,
|
||||||
|
"read": 1,
|
||||||
|
"report": 0,
|
||||||
|
"role": "Accounts User",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC"
|
||||||
|
}
|
115
erpnext/accounts/doctype/payment_tool/payment_tool.py
Normal file
115
erpnext/accounts/doctype/payment_tool/payment_tool.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _, scrub
|
||||||
|
from frappe.utils import flt
|
||||||
|
from frappe.model.document import Document
|
||||||
|
import json
|
||||||
|
|
||||||
|
class PaymentTool(Document):
|
||||||
|
def make_journal_voucher(self):
|
||||||
|
from erpnext.accounts.utils import get_balance_on
|
||||||
|
total_payment_amount = 0.00
|
||||||
|
invoice_voucher_type = {
|
||||||
|
'Sales Invoice': 'against_invoice',
|
||||||
|
'Purchase Invoice': 'against_voucher',
|
||||||
|
'Journal Voucher': 'against_jv',
|
||||||
|
'Sales Order': 'against_sales_order',
|
||||||
|
'Purchase Order': 'against_purchase_order',
|
||||||
|
}
|
||||||
|
|
||||||
|
jv = frappe.new_doc('Journal Voucher')
|
||||||
|
jv.voucher_type = 'Journal Entry'
|
||||||
|
jv.company = self.company
|
||||||
|
jv.cheque_no = self.reference_no
|
||||||
|
jv.cheque_date = self.reference_date
|
||||||
|
|
||||||
|
if not self.total_payment_amount:
|
||||||
|
frappe.throw(_("Please enter Payment Amount in atleast one row"))
|
||||||
|
|
||||||
|
for v in self.get("payment_tool_details"):
|
||||||
|
if not frappe.db.get_value(v.against_voucher_type, {"name": v.against_voucher_no}):
|
||||||
|
frappe.throw(_("Row {0}: {1} is not a valid {2}").format(v.idx, v.against_voucher_no,
|
||||||
|
v.against_voucher_type))
|
||||||
|
|
||||||
|
if v.payment_amount:
|
||||||
|
d1 = jv.append("entries")
|
||||||
|
d1.account = self.party_account
|
||||||
|
d1.balance = get_balance_on(self.party_account)
|
||||||
|
d1.set("debit" if self.received_or_paid=="Paid" else "credit", flt(v.payment_amount))
|
||||||
|
d1.set(invoice_voucher_type.get(v.against_voucher_type), v.against_voucher_no)
|
||||||
|
d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No')
|
||||||
|
total_payment_amount = flt(total_payment_amount) + flt(d1.debit) - flt(d1.credit)
|
||||||
|
|
||||||
|
d2 = jv.append("entries")
|
||||||
|
d2.account = self.payment_account
|
||||||
|
d2.set('debit' if total_payment_amount < 0 else 'credit', abs(total_payment_amount))
|
||||||
|
if self.payment_account:
|
||||||
|
d2.balance = get_balance_on(self.payment_account)
|
||||||
|
|
||||||
|
return jv.as_dict()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_party_account(party_type, party_name):
|
||||||
|
return frappe.db.get_value("Account", {"master_type": party_type, "master_name": party_name})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_outstanding_vouchers(args):
|
||||||
|
from erpnext.accounts.utils import get_outstanding_invoices
|
||||||
|
|
||||||
|
args = json.loads(args)
|
||||||
|
|
||||||
|
if args.get("party_type") == "Customer" and args.get("received_or_paid") == "Received":
|
||||||
|
amount_query = "ifnull(debit, 0) - ifnull(credit, 0)"
|
||||||
|
elif args.get("party_type") == "Supplier" and args.get("received_or_paid") == "Paid":
|
||||||
|
amount_query = "ifnull(credit, 0) - ifnull(debit, 0)"
|
||||||
|
else:
|
||||||
|
frappe.throw(_("Please enter the Against Vouchers manually"))
|
||||||
|
|
||||||
|
# Get all outstanding sales /purchase invoices
|
||||||
|
outstanding_invoices = get_outstanding_invoices(amount_query, args.get("party_account"))
|
||||||
|
|
||||||
|
# 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_name"))
|
||||||
|
return outstanding_invoices + orders_to_be_billed
|
||||||
|
|
||||||
|
def get_orders_to_be_billed(party_type, party_name):
|
||||||
|
voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order'
|
||||||
|
orders = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
name as voucher_no,
|
||||||
|
ifnull(grand_total, 0) as invoice_amount,
|
||||||
|
(ifnull(grand_total, 0) - ifnull(advance_paid, 0)) as outstanding_amount,
|
||||||
|
transaction_date as posting_date
|
||||||
|
from
|
||||||
|
`tab%s`
|
||||||
|
where
|
||||||
|
%s = %s
|
||||||
|
and docstatus = 1
|
||||||
|
and ifnull(grand_total, 0) > ifnull(advance_paid, 0)
|
||||||
|
and ifnull(per_billed, 0) < 100.0
|
||||||
|
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'),
|
||||||
|
party_name, as_dict = True)
|
||||||
|
|
||||||
|
order_list = []
|
||||||
|
for d in orders:
|
||||||
|
d["voucher_type"] = voucher_type
|
||||||
|
order_list.append(d)
|
||||||
|
|
||||||
|
return order_list
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_against_voucher_amount(against_voucher_type, against_voucher_no):
|
||||||
|
if against_voucher_type in ["Sales Order", "Purchase Order"]:
|
||||||
|
select_cond = "grand_total as total_amount, ifnull(grand_total, 0) - ifnull(advance_paid, 0) as outstanding_amount"
|
||||||
|
elif against_voucher_type in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
|
select_cond = "grand_total as total_amount, outstanding_amount"
|
||||||
|
elif against_voucher_type == "Journal Voucher":
|
||||||
|
select_cond = "total_debit as total_amount"
|
||||||
|
|
||||||
|
details = frappe.db.sql("""select {0} from `tab{1}` where name = %s"""
|
||||||
|
.format(select_cond, against_voucher_type), against_voucher_no, as_dict=1)
|
||||||
|
|
||||||
|
return details[0] if details else {}
|
193
erpnext/accounts/doctype/payment_tool/test_payment_tool.py
Normal file
193
erpnext/accounts/doctype/payment_tool/test_payment_tool.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
# 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 unittest, frappe, json
|
||||||
|
from frappe.utils import flt
|
||||||
|
|
||||||
|
test_dependencies = ["Item"]
|
||||||
|
|
||||||
|
class TestPaymentTool(unittest.TestCase):
|
||||||
|
def test_make_journal_voucher(self):
|
||||||
|
from erpnext.accounts.doctype.journal_voucher.test_journal_voucher \
|
||||||
|
import test_records as jv_test_records
|
||||||
|
from erpnext.selling.doctype.sales_order.test_sales_order \
|
||||||
|
import test_records as so_test_records
|
||||||
|
from erpnext.buying.doctype.purchase_order.test_purchase_order \
|
||||||
|
import test_records as po_test_records
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice \
|
||||||
|
import test_records as si_test_records
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
|
||||||
|
import test_records as pi_test_records
|
||||||
|
|
||||||
|
self.clear_table_entries()
|
||||||
|
|
||||||
|
base_customer_jv = self.create_against_jv(jv_test_records[2], { "account": "_Test Customer 3 - _TC"})
|
||||||
|
base_supplier_jv = self.create_against_jv(jv_test_records[1], { "account": "_Test Supplier 1 - _TC"})
|
||||||
|
|
||||||
|
|
||||||
|
#Create SO with partial outstanding
|
||||||
|
so1 = self.create_voucher(so_test_records[0], {
|
||||||
|
"customer": "_Test Customer 3"
|
||||||
|
})
|
||||||
|
|
||||||
|
jv_against_so1 = self.create_against_jv(jv_test_records[0], {
|
||||||
|
"account": "_Test Customer 3 - _TC",
|
||||||
|
"against_sales_order": so1.name
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
#Create SO with no outstanding
|
||||||
|
so2 = self.create_voucher(so_test_records[0], {
|
||||||
|
"customer": "_Test Customer 3"
|
||||||
|
})
|
||||||
|
|
||||||
|
jv_against_so2 = self.create_against_jv(jv_test_records[0], {
|
||||||
|
"account": "_Test Customer 3 - _TC",
|
||||||
|
"against_sales_order": so2.name,
|
||||||
|
"credit": 1000
|
||||||
|
})
|
||||||
|
po = self.create_voucher(po_test_records[1], {
|
||||||
|
"supplier": "_Test Supplier 1"
|
||||||
|
})
|
||||||
|
|
||||||
|
#Create SI with partial outstanding
|
||||||
|
si1 = self.create_voucher(si_test_records[0], {
|
||||||
|
"customer": "_Test Customer 3",
|
||||||
|
"debit_to": "_Test Customer 3 - _TC"
|
||||||
|
})
|
||||||
|
|
||||||
|
jv_against_si1 = self.create_against_jv(jv_test_records[0], {
|
||||||
|
"account": "_Test Customer 3 - _TC",
|
||||||
|
"against_invoice": si1.name
|
||||||
|
})
|
||||||
|
#Create SI with no outstanding
|
||||||
|
si2 = self.create_voucher(si_test_records[0], {
|
||||||
|
"customer": "_Test Customer 3",
|
||||||
|
"debit_to": "_Test Customer 3 - _TC"
|
||||||
|
})
|
||||||
|
|
||||||
|
jv_against_si2 = self.create_against_jv(jv_test_records[0], {
|
||||||
|
"account": "_Test Customer 3 - _TC",
|
||||||
|
"against_invoice": si2.name,
|
||||||
|
"credit": 561.80
|
||||||
|
})
|
||||||
|
|
||||||
|
pi = self.create_voucher(pi_test_records[0], {
|
||||||
|
"supplier": "_Test Supplier 1",
|
||||||
|
"credit_to": "_Test Supplier 1 - _TC"
|
||||||
|
})
|
||||||
|
|
||||||
|
#Create a dict containing properties and expected values
|
||||||
|
expected_outstanding = {
|
||||||
|
"Journal Voucher" : [base_customer_jv.name, 400.00],
|
||||||
|
"Sales Invoice" : [si1.name, 161.80],
|
||||||
|
"Purchase Invoice" : [pi.name, 1512.30],
|
||||||
|
"Sales Order" : [so1.name, 600.00],
|
||||||
|
"Purchase Order" : [po.name, 5000.00]
|
||||||
|
}
|
||||||
|
|
||||||
|
args = {
|
||||||
|
"company": "_Test Company",
|
||||||
|
"party_type": "Customer",
|
||||||
|
"received_or_paid": "Received",
|
||||||
|
"customer": "_Test Customer",
|
||||||
|
"party_account": "_Test Customer 3 - _TC",
|
||||||
|
"payment_mode": "Cheque",
|
||||||
|
"payment_account": "_Test Account Bank Account - _TC",
|
||||||
|
"reference_no": "123456",
|
||||||
|
"reference_date": "2013-02-14"
|
||||||
|
}
|
||||||
|
|
||||||
|
self.make_voucher_for_party(args, expected_outstanding)
|
||||||
|
|
||||||
|
args.update({
|
||||||
|
"party_type": "Supplier",
|
||||||
|
"received_or_paid": "Paid",
|
||||||
|
"supplier": "_Test Supplier 1",
|
||||||
|
"party_account": "_Test Supplier 1 - _TC"
|
||||||
|
})
|
||||||
|
expected_outstanding["Journal Voucher"] = [base_supplier_jv.name, 400.00]
|
||||||
|
self.make_voucher_for_party(args, expected_outstanding)
|
||||||
|
|
||||||
|
def create_voucher(self, test_record, args):
|
||||||
|
doc = frappe.copy_doc(test_record)
|
||||||
|
doc.update(args)
|
||||||
|
doc.insert()
|
||||||
|
doc.submit()
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def create_against_jv(self, test_record, args):
|
||||||
|
jv = frappe.copy_doc(test_record)
|
||||||
|
jv.get("entries")[0].update(args)
|
||||||
|
if args.get("debit"):
|
||||||
|
jv.get("entries")[1].credit = args["debit"]
|
||||||
|
elif args.get("credit"):
|
||||||
|
jv.get("entries")[1].debit = args["credit"]
|
||||||
|
|
||||||
|
jv.insert()
|
||||||
|
jv.submit()
|
||||||
|
return jv
|
||||||
|
|
||||||
|
def make_voucher_for_party(self, args, expected_outstanding):
|
||||||
|
#Make Journal Voucher for Party
|
||||||
|
payment_tool_doc = frappe.new_doc("Payment Tool")
|
||||||
|
|
||||||
|
for k, v in args.items():
|
||||||
|
payment_tool_doc.set(k, v)
|
||||||
|
|
||||||
|
self.check_outstanding_vouchers(payment_tool_doc, args, expected_outstanding)
|
||||||
|
|
||||||
|
|
||||||
|
def check_outstanding_vouchers(self, doc, args, expected_outstanding):
|
||||||
|
from erpnext.accounts.doctype.payment_tool.payment_tool import get_outstanding_vouchers
|
||||||
|
|
||||||
|
outstanding_entries = get_outstanding_vouchers(json.dumps(args))
|
||||||
|
|
||||||
|
for d in outstanding_entries:
|
||||||
|
self.assertEquals(flt(d.get("outstanding_amount"), 2), expected_outstanding.get(d.get("voucher_type"))[1])
|
||||||
|
|
||||||
|
self.check_jv_entries(doc, outstanding_entries, expected_outstanding)
|
||||||
|
|
||||||
|
def check_jv_entries(self, paytool, outstanding_entries, expected_outstanding):
|
||||||
|
for e in outstanding_entries:
|
||||||
|
d1 = paytool.append("payment_tool_details")
|
||||||
|
d1.against_voucher_type = e.get("voucher_type")
|
||||||
|
d1.against_voucher_no = e.get("voucher_no")
|
||||||
|
d1.total_amount = e.get("invoice_amount")
|
||||||
|
d1.outstanding_amount = e.get("outstanding_amount")
|
||||||
|
d1.payment_amount = 100.00
|
||||||
|
paytool.total_payment_amount = 300
|
||||||
|
|
||||||
|
new_jv = paytool.make_journal_voucher()
|
||||||
|
|
||||||
|
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
||||||
|
#against_voucher, against_sales_order, against_purchase_order]
|
||||||
|
expected_values = [
|
||||||
|
[paytool.party_account, 100.00, expected_outstanding.get("Journal Voucher")[0], None, None, None, None],
|
||||||
|
[paytool.party_account, 100.00, None, expected_outstanding.get("Sales Invoice")[0], None, None, None],
|
||||||
|
[paytool.party_account, 100.00, None, None, expected_outstanding.get("Purchase Invoice")[0], None, None],
|
||||||
|
[paytool.party_account, 100.00, None, None, None, expected_outstanding.get("Sales Order")[0], None],
|
||||||
|
[paytool.party_account, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]]
|
||||||
|
]
|
||||||
|
|
||||||
|
for jv_entry in new_jv.get("entries"):
|
||||||
|
if paytool.party_account == jv_entry.get("account"):
|
||||||
|
row = [
|
||||||
|
jv_entry.get("account"),
|
||||||
|
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit"),
|
||||||
|
jv_entry.get("against_jv"),
|
||||||
|
jv_entry.get("against_invoice"),
|
||||||
|
jv_entry.get("against_voucher"),
|
||||||
|
jv_entry.get("against_sales_order"),
|
||||||
|
jv_entry.get("against_purchase_order"),
|
||||||
|
]
|
||||||
|
self.assertTrue(row in expected_values)
|
||||||
|
|
||||||
|
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
||||||
|
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
||||||
|
|
||||||
|
def clear_table_entries(self):
|
||||||
|
frappe.db.sql("""delete from `tabGL Entry` where (account = "_Test Customer 3 - _TC" or account = "_Test Supplier 1 - _TC")""")
|
||||||
|
frappe.db.sql("""delete from `tabSales Order` where customer_name = "_Test Customer 3" """)
|
||||||
|
frappe.db.sql("""delete from `tabPurchase Order` where supplier_name = "_Test Supplier 1" """)
|
@ -0,0 +1,125 @@
|
|||||||
|
{
|
||||||
|
"allow_attach": 0,
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"creation": "2014-08-11 14:27:54.463897",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "against_voucher_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Against Voucher Type",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher\nSales Order\nPurchase Order",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_width": "",
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"width": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "against_voucher_no",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Against Voucher No",
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "[Select]",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "total_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Total Amount",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "outstanding_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Outstanding Amount",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"fieldname": "payment_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Payment Amount",
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 1,
|
||||||
|
"modified": "2014-08-20 12:32:29.842215",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Payment Tool Detail",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC"
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class PaymentToolDetail(Document):
|
||||||
|
pass
|
@ -391,3 +391,42 @@ def get_stock_rbnb_difference(posting_date, company):
|
|||||||
|
|
||||||
# Amount should be credited
|
# Amount should be credited
|
||||||
return flt(stock_rbnb) + flt(sys_bal)
|
return flt(stock_rbnb) + flt(sys_bal)
|
||||||
|
|
||||||
|
def get_outstanding_invoices(amount_query, account):
|
||||||
|
all_outstanding_vouchers = []
|
||||||
|
outstanding_voucher_list = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
voucher_no, voucher_type, posting_date,
|
||||||
|
ifnull(sum({amount_query}), 0) as invoice_amount
|
||||||
|
from
|
||||||
|
`tabGL Entry`
|
||||||
|
where
|
||||||
|
account = %s and {amount_query} > 0
|
||||||
|
group by voucher_type, voucher_no
|
||||||
|
""".format(amount_query = amount_query), account, as_dict = True)
|
||||||
|
|
||||||
|
for d in outstanding_voucher_list:
|
||||||
|
payment_amount = frappe.db.sql("""
|
||||||
|
select ifnull(sum(ifnull({amount_query}, 0)), 0)
|
||||||
|
from
|
||||||
|
`tabGL Entry`
|
||||||
|
where
|
||||||
|
account = %s and {amount_query} < 0
|
||||||
|
and against_voucher_type = %s and ifnull(against_voucher, '') = %s
|
||||||
|
""".format(**{
|
||||||
|
"amount_query": amount_query
|
||||||
|
}), (account, d.voucher_type, d.voucher_no))
|
||||||
|
|
||||||
|
payment_amount = -1*payment_amount[0][0] if payment_amount else 0
|
||||||
|
|
||||||
|
if d.invoice_amount > payment_amount:
|
||||||
|
|
||||||
|
all_outstanding_vouchers.append({
|
||||||
|
'voucher_no': d.voucher_no,
|
||||||
|
'voucher_type': d.voucher_type,
|
||||||
|
'posting_date': d.posting_date,
|
||||||
|
'invoice_amount': flt(d.invoice_amount),
|
||||||
|
'outstanding_amount': d.invoice_amount - payment_amount
|
||||||
|
})
|
||||||
|
|
||||||
|
return all_outstanding_vouchers
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -238,6 +238,11 @@ def make_purchase_receipt(source_name, target_doc=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_purchase_invoice(source_name, target_doc=None):
|
def make_purchase_invoice(source_name, target_doc=None):
|
||||||
|
def postprocess(source, target):
|
||||||
|
set_missing_values(source, target)
|
||||||
|
#Get the advance paid Journal Vouchers in Purchase Invoice Advance
|
||||||
|
target.get_advances()
|
||||||
|
|
||||||
def update_item(obj, target, source_parent):
|
def update_item(obj, target, source_parent):
|
||||||
target.amount = flt(obj.amount) - flt(obj.billed_amt)
|
target.amount = flt(obj.amount) - flt(obj.billed_amt)
|
||||||
target.base_amount = target.amount * flt(source_parent.conversion_rate)
|
target.base_amount = target.amount * flt(source_parent.conversion_rate)
|
||||||
@ -263,6 +268,6 @@ def make_purchase_invoice(source_name, target_doc=None):
|
|||||||
"doctype": "Purchase Taxes and Charges",
|
"doctype": "Purchase Taxes and Charges",
|
||||||
"add_if_empty": True
|
"add_if_empty": True
|
||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, postprocess)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"advance_paid": 0.0,
|
||||||
"buying_price_list": "_Test Price List",
|
"buying_price_list": "_Test Price List",
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"conversion_rate": 1.0,
|
"conversion_rate": 1.0,
|
||||||
@ -31,5 +32,39 @@
|
|||||||
"supplier": "_Test Supplier",
|
"supplier": "_Test Supplier",
|
||||||
"supplier_name": "_Test Supplier",
|
"supplier_name": "_Test Supplier",
|
||||||
"transaction_date": "2013-02-12"
|
"transaction_date": "2013-02-12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"advance_paid": 0.0,
|
||||||
|
"buying_price_list": "_Test Price List",
|
||||||
|
"company": "_Test Company",
|
||||||
|
"conversion_rate": 1.0,
|
||||||
|
"currency": "INR",
|
||||||
|
"doctype": "Purchase Order",
|
||||||
|
"fiscal_year": "_Test Fiscal Year 2013",
|
||||||
|
"grand_total": 5000.0,
|
||||||
|
"grand_total_import": 5000.0,
|
||||||
|
"is_subcontracted": "No",
|
||||||
|
"naming_series": "_T-Purchase Order-",
|
||||||
|
"net_total": 5000.0,
|
||||||
|
"po_details": [
|
||||||
|
{
|
||||||
|
"base_amount": 5000.0,
|
||||||
|
"conversion_factor": 1.0,
|
||||||
|
"description": "_Test Item",
|
||||||
|
"doctype": "Purchase Order Item",
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"item_name": "_Test Item",
|
||||||
|
"parentfield": "po_details",
|
||||||
|
"qty": 10.0,
|
||||||
|
"rate": 500.0,
|
||||||
|
"schedule_date": "2013-03-01",
|
||||||
|
"stock_uom": "_Test UOM",
|
||||||
|
"uom": "_Test UOM",
|
||||||
|
"warehouse": "_Test Warehouse - _TC"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"supplier": "_Test Supplier",
|
||||||
|
"supplier_name": "_Test Supplier",
|
||||||
|
"transaction_date": "2013-02-12"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -4,5 +4,11 @@
|
|||||||
"doctype": "Supplier",
|
"doctype": "Supplier",
|
||||||
"supplier_name": "_Test Supplier",
|
"supplier_name": "_Test Supplier",
|
||||||
"supplier_type": "_Test Supplier Type"
|
"supplier_type": "_Test Supplier Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"doctype": "Supplier",
|
||||||
|
"supplier_name": "_Test Supplier 1",
|
||||||
|
"supplier_type": "_Test Supplier Type"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -61,6 +61,11 @@ def get_data():
|
|||||||
"name": "Period Closing Voucher",
|
"name": "Period Closing Voucher",
|
||||||
"description": _("Close Balance Sheet and book Profit or Loss.")
|
"description": _("Close Balance Sheet and book Profit or Loss.")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Payment Tool",
|
||||||
|
"description": _("Create Payment Entries against Orders or Invoices.")
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -363,9 +363,10 @@ class AccountsController(TransactionBase):
|
|||||||
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
||||||
|
|
||||||
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr):
|
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr):
|
||||||
|
against_order_list = []
|
||||||
res = frappe.db.sql("""
|
res = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no
|
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no, t2.%s as order_no
|
||||||
from
|
from
|
||||||
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
||||||
where
|
where
|
||||||
@ -374,18 +375,25 @@ class AccountsController(TransactionBase):
|
|||||||
and ifnull(t2.against_invoice, '') = ''
|
and ifnull(t2.against_invoice, '') = ''
|
||||||
and ifnull(t2.against_jv, '') = ''
|
and ifnull(t2.against_jv, '') = ''
|
||||||
order by t1.posting_date""" %
|
order by t1.posting_date""" %
|
||||||
(dr_or_cr, '%s'), account_head, as_dict=1)
|
(dr_or_cr, "against_sales_order" if dr_or_cr == "credit" \
|
||||||
|
else "against_purchase_order", '%s'),
|
||||||
|
account_head, as_dict= True)
|
||||||
|
|
||||||
|
if self.get("entries"):
|
||||||
|
for i in self.get("entries"):
|
||||||
|
against_order_list.append(i.sales_order if dr_or_cr == "credit" else i.purchase_order)
|
||||||
|
|
||||||
self.set(parentfield, [])
|
self.set(parentfield, [])
|
||||||
for d in res:
|
for d in res:
|
||||||
self.append(parentfield, {
|
if not against_order_list or d.order_no in against_order_list:
|
||||||
"doctype": child_doctype,
|
self.append(parentfield, {
|
||||||
"journal_voucher": d.jv_no,
|
"doctype": child_doctype,
|
||||||
"jv_detail_no": d.jv_detail_no,
|
"journal_voucher": d.jv_no,
|
||||||
"remarks": d.remark,
|
"jv_detail_no": d.jv_detail_no,
|
||||||
"advance_amount": flt(d.amount),
|
"remarks": d.remark,
|
||||||
"allocate_amount": 0
|
"advance_amount": flt(d.amount),
|
||||||
})
|
"allocate_amount": 0
|
||||||
|
})
|
||||||
|
|
||||||
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
|
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
|
||||||
from erpnext.controllers.status_updater import get_tolerance_for
|
from erpnext.controllers.status_updater import get_tolerance_for
|
||||||
@ -430,6 +438,32 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
return stock_items
|
return stock_items
|
||||||
|
|
||||||
|
def set_total_advance_paid(self):
|
||||||
|
if self.doctype == "Sales Order":
|
||||||
|
dr_or_cr = "credit"
|
||||||
|
against_field = "against_sales_order"
|
||||||
|
else:
|
||||||
|
dr_or_cr = "debit"
|
||||||
|
against_field = "against_purchase_order"
|
||||||
|
|
||||||
|
advance_paid = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
sum(ifnull({dr_or_cr}, 0))
|
||||||
|
from
|
||||||
|
`tabJournal Voucher Detail`
|
||||||
|
where
|
||||||
|
{against_field} = %s and docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr, \
|
||||||
|
against_field=against_field), self.name)
|
||||||
|
|
||||||
|
if advance_paid:
|
||||||
|
advance_paid = flt(advance_paid[0][0], self.precision("advance_paid"))
|
||||||
|
if flt(self.grand_total) >= advance_paid:
|
||||||
|
frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
|
||||||
|
else:
|
||||||
|
frappe.throw(_("Total advance ({0}) against Order {1} cannot be greater \
|
||||||
|
than the Grand Total ({2})")
|
||||||
|
.format(advance_paid, self.name, self.grand_total))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def company_abbr(self):
|
def company_abbr(self):
|
||||||
if not hasattr(self, "_abbr"):
|
if not hasattr(self, "_abbr"):
|
||||||
|
@ -22,5 +22,13 @@
|
|||||||
"customer_type": "Individual",
|
"customer_type": "Individual",
|
||||||
"doctype": "Customer",
|
"doctype": "Customer",
|
||||||
"territory": "_Test Territory"
|
"territory": "_Test Territory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"customer_group": "_Test Customer Group",
|
||||||
|
"customer_name": "_Test Customer 3",
|
||||||
|
"customer_type": "Individual",
|
||||||
|
"doctype": "Customer",
|
||||||
|
"territory": "_Test Territory"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -326,6 +326,11 @@ def make_delivery_note(source_name, target_doc=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_sales_invoice(source_name, target_doc=None):
|
def make_sales_invoice(source_name, target_doc=None):
|
||||||
|
def postprocess(source, target):
|
||||||
|
set_missing_values(source, target)
|
||||||
|
#Get the advance paid Journal Vouchers in Sales Invoice Advance
|
||||||
|
target.get_advances()
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.is_pos = 0
|
target.is_pos = 0
|
||||||
target.ignore_pricing_rule = 1
|
target.ignore_pricing_rule = 1
|
||||||
@ -361,7 +366,18 @@ def make_sales_invoice(source_name, target_doc=None):
|
|||||||
"doctype": "Sales Team",
|
"doctype": "Sales Team",
|
||||||
"add_if_empty": True
|
"add_if_empty": True
|
||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, postprocess)
|
||||||
|
|
||||||
|
def set_advance_vouchers(source, target):
|
||||||
|
advance_voucher_list = []
|
||||||
|
|
||||||
|
advance_voucher = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
t1.name as voucher_no, t1.posting_date, t1.remark, t2.account,
|
||||||
|
t2.name as voucher_detail_no, {amount_query} as payment_amount, t2.is_advance
|
||||||
|
from
|
||||||
|
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
||||||
|
""")
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"advance_paid": 0.0,
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"conversion_rate": 1.0,
|
"conversion_rate": 1.0,
|
||||||
"currency": "INR",
|
"currency": "INR",
|
||||||
|
@ -223,8 +223,8 @@ def validate_serial_no(sle, item_det):
|
|||||||
sr = frappe.get_doc("Serial No", serial_no)
|
sr = frappe.get_doc("Serial No", serial_no)
|
||||||
|
|
||||||
if sr.item_code!=sle.item_code:
|
if sr.item_code!=sle.item_code:
|
||||||
frappe.throw(_("Serial No {0} does not belong to Item {1}").format(sle.item_code,
|
frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
|
||||||
serial_no), SerialNoItemError)
|
sle.item_code), SerialNoItemError)
|
||||||
|
|
||||||
if sr.warehouse and sle.actual_qty > 0:
|
if sr.warehouse and sle.actual_qty > 0:
|
||||||
frappe.throw(_("Serial No {0} has already been received").format(sr.name),
|
frappe.throw(_("Serial No {0} has already been received").format(sr.name),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user