diff --git a/erpnext/__version__.py b/erpnext/__version__.py
index 64f6e28028..0d53216d5b 100644
--- a/erpnext/__version__.py
+++ b/erpnext/__version__.py
@@ -1 +1 @@
-__version__ = '4.7.2'
+__version__ = '4.8.0'
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index dab2d82092..4cc3d572ae 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -124,6 +124,10 @@ def update_outstanding_amt(account, against_voucher_type, against_voucher, on_ca
from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s
and account = %s and ifnull(against_voucher, '') = ''""",
(against_voucher, account))[0][0])
+ if not against_voucher_amount:
+ frappe.throw(_("Against Journal Voucher {0} is already adjusted against some other voucher")
+ .format(against_voucher))
+
bal = against_voucher_amount + bal
if against_voucher_amount < 0:
bal = -bal
diff --git a/erpnext/accounts/doctype/journal_voucher/journal_voucher.py b/erpnext/accounts/doctype/journal_voucher/journal_voucher.py
index 3c67508306..f9f6df1557 100644
--- a/erpnext/accounts/doctype/journal_voucher/journal_voucher.py
+++ b/erpnext/accounts/doctype/journal_voucher/journal_voucher.py
@@ -88,15 +88,24 @@ class JournalVoucher(AccountsController):
def validate_against_jv(self):
for d in self.get('entries'):
if d.against_jv:
+ account_root_type = frappe.db.get_value("Account", d.account, "root_type")
+ if account_root_type == "Asset" and flt(d.debit) > 0:
+ frappe.throw(_("For {0}, only credit entries can be linked against another debit entry")
+ .format(d.account))
+ elif account_root_type == "Liability" and flt(d.credit) > 0:
+ frappe.throw(_("For {0}, only debit entries can be linked against another credit entry")
+ .format(d.account))
+
if d.against_jv == self.name:
frappe.throw(_("You can not enter current voucher in 'Against Journal Voucher' column"))
against_entries = frappe.db.sql("""select * from `tabJournal Voucher Detail`
where account = %s and docstatus = 1 and parent = %s
- and ifnull(against_jv, '') = ''""", (d.account, d.against_jv), as_dict=True)
+ and ifnull(against_jv, '') = '' and ifnull(against_invoice, '') = ''
+ and ifnull(against_voucher, '') = ''""", (d.account, d.against_jv), as_dict=True)
if not against_entries:
- frappe.throw(_("Journal Voucher {0} does not have account {1} or already matched")
+ frappe.throw(_("Journal Voucher {0} does not have account {1} or already matched against other voucher")
.format(d.against_jv, d.account))
else:
dr_or_cr = "debit" if d.credit > 0 else "credit"
@@ -153,7 +162,7 @@ class JournalVoucher(AccountsController):
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") \
@@ -165,7 +174,7 @@ class JournalVoucher(AccountsController):
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,
+ voucher_properties = frappe.db.get_value(doctype, voucher_no,
["docstatus", "outstanding_amount"])
if voucher_properties[0] != 1:
@@ -177,7 +186,7 @@ class JournalVoucher(AccountsController):
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,
+ voucher_properties = frappe.db.get_value(doctype, voucher_no,
["docstatus", "per_billed", "status", "advance_paid", "grand_total"])
if voucher_properties[0] != 1:
@@ -532,9 +541,10 @@ def get_against_sales_invoice(doctype, txt, searchfield, start, page_len, filter
(filters["account"], "%%%s%%" % txt, start, page_len))
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
- return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
- from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jv_detail
- where jv_detail.parent = jv.name and jv_detail.account = %s and jv.docstatus = 1
+ return frappe.db.sql("""select distinct jv.name, jv.posting_date, jv.user_remark
+ from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jvd
+ where jvd.parent = jv.name and jvd.account = %s and jv.docstatus = 1
+ and (ifnull(jvd.against_invoice, '') = '' and ifnull(jvd.against_voucher, '') = '' and ifnull(jvd.against_jv, '') = '' )
and jv.%s like %s order by jv.name desc limit %s, %s""" %
("%s", searchfield, "%s", "%s", "%s"),
(filters["account"], "%%%s%%" % txt, start, page_len))
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 97484da468..bfcd63ea01 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -19,9 +19,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
]
};
}
-
+
});
-
+
this.frm.set_query('bank_cash_account', function() {
if(!me.frm.doc.company) {
msgprint(__("Please select company first"));
@@ -35,12 +35,8 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
};
}
});
-
- var help_content = ' ' + __("Note") + ':
'+
- '
' + __("If you are unable to match the exact amount, then amend your Journal Voucher and split rows such that payment amount match the invoice amount.") + '
';
- this.frm.set_value("reconcile_help", help_content);
},
-
+
get_unreconciled_entries: function() {
var me = this;
return this.frm.call({
@@ -48,12 +44,12 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
method: 'get_unreconciled_entries',
callback: function(r, rt) {
var invoices = [];
-
+
$.each(me.frm.doc.payment_reconciliation_invoices || [], function(i, row) {
- if (row.invoice_number && !inList(invoices, row.invoice_number))
+ if (row.invoice_number && !inList(invoices, row.invoice_number))
invoices.push(row.invoice_number);
});
-
+
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
me.frm.doc.name).options = invoices.join("\n");
@@ -79,4 +75,4 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
-cur_frm.add_fetch('party_account', 'master_type', 'party_type')
\ No newline at end of file
+cur_frm.add_fetch('party_account', 'master_type', 'party_type')
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
index 51cb306157..bb07b4695b 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
@@ -118,19 +118,12 @@
"options": "Payment Reconciliation Invoice",
"permlevel": 0,
"read_only": 1
- },
- {
- "fieldname": "reconcile_help",
- "fieldtype": "Small Text",
- "label": "",
- "permlevel": 0,
- "read_only": 1
}
],
"hide_toolbar": 1,
"icon": "icon-resize-horizontal",
"issingle": 1,
- "modified": "2014-07-31 05:43:03.410832",
+ "modified": "2014-10-16 17:51:44.367107",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation",
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index a5a56aee0a..c6a2b056da 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -124,7 +124,7 @@ class PaymentReconciliation(Document):
dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
lst = []
for e in self.get('payment_reconciliation_payments'):
- if e.invoice_type and e.invoice_number:
+ if e.invoice_type and e.invoice_number and e.allocated_amount:
lst.append({
'voucher_no' : e.journal_voucher,
'voucher_detail_no' : e.voucher_detail_number,
@@ -134,7 +134,7 @@ class PaymentReconciliation(Document):
'is_advance' : e.is_advance,
'dr_or_cr' : dr_or_cr,
'unadjusted_amt' : flt(e.amount),
- 'allocated_amt' : flt(e.amount)
+ 'allocated_amt' : flt(e.allocated_amount)
})
if lst:
@@ -162,18 +162,23 @@ class PaymentReconciliation(Document):
invoices_to_reconcile = []
for p in self.get("payment_reconciliation_payments"):
- if p.invoice_type and p.invoice_number:
+ if p.invoice_type and p.invoice_number and p.allocated_amount:
invoices_to_reconcile.append(p.invoice_number)
if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
frappe.throw(_("{0}: {1} not found in Invoice Details table")
.format(p.invoice_type, p.invoice_number))
- if p.amount > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
- frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount. Please refer Note below.").format(p.idx))
+ if flt(p.allocated_amount) > flt(p.amount):
+ frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to JV amount {2}")
+ .format(p.idx, p.allocated_amount, p.amount))
+
+ if flt(p.allocated_amount) > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
+ frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2}")
+ .format(p.idx, p.allocated_amount, unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)))
if not invoices_to_reconcile:
- frappe.throw(_("Please select Invoice Type and Invoice Number in atleast one row"))
+ frappe.throw(_("Please select Allocated Amount, Invoice Type and Invoice Number in atleast one row"))
def check_condition(self, dr_or_cr):
cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or ""
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
index 73fd0f50f3..6f576ca38b 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
@@ -53,11 +53,20 @@
"label": "Column Break",
"permlevel": 0
},
+ {
+ "fieldname": "allocated_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Allocated amount",
+ "permlevel": 0,
+ "precision": "",
+ "reqd": 1
+ },
{
"default": "Sales Invoice",
"fieldname": "invoice_type",
"fieldtype": "Select",
- "in_list_view": 1,
+ "in_list_view": 0,
"label": "Invoice Type",
"options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher",
"permlevel": 0,
@@ -95,7 +104,7 @@
}
],
"istable": 1,
- "modified": "2014-07-21 16:53:56.206169",
+ "modified": "2014-10-16 17:40:54.040194",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Payment",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index fe989d85dd..febf4b0a49 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -4,7 +4,7 @@ app_publisher = "Web Notes Technologies Pvt. Ltd. and Contributors"
app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
app_icon = "icon-th"
app_color = "#e74c3c"
-app_version = "4.7.2"
+app_version = "4.8.0"
error_report_email = "support@erpnext.com"
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 864329d3ea..079b7fc110 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -245,7 +245,7 @@ class DeliveryNote(SellingController):
sl_entries = []
for d in self.get_item_list():
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
- and d.warehouse:
+ and d.warehouse and flt(d['qty']):
self.update_reserved_qty(d)
sl_entries.append(self.get_sl_entries(d, {
diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py
index 643bec986a..f1ba1798ce 100644
--- a/erpnext/utilities/repost_stock.py
+++ b/erpnext/utilities/repost_stock.py
@@ -73,7 +73,7 @@ def get_reserved_qty(item_code, warehouse):
from `tabPacked Item` dnpi_in
where item_code = %s and warehouse = %s
and parenttype="Sales Order"
- and item_code != parent_item
+ and item_code != parent_item
and exists (select * from `tabSales Order` so
where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped')
) dnpi)
diff --git a/setup.py b/setup.py
index e567ea2e76..5c7fe9a77d 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
from setuptools import setup, find_packages
import os
-version = "4.7.2"
+version = "4.8.0"
with open("requirements.txt", "r") as f:
install_requires = f.readlines()
diff --git a/test_sites/test_site/site_config.json b/test_sites/test_site/site_config.json
index 05bf562766..12007b87ec 100644
--- a/test_sites/test_site/site_config.json
+++ b/test_sites/test_site/site_config.json
@@ -1,5 +1,6 @@
{
"db_name": "test_frappe",
"db_password": "test_frappe",
+ "admin_password": "admin",
"mute_emails": 1
}