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") + ':
'+ - ''; - 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 }