Merge pull request #2322 from nabinhait/stock_reco
Partial payment reconciliation
This commit is contained in:
commit
4595c30a7b
@ -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
|
from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s
|
||||||
and account = %s and ifnull(against_voucher, '') = ''""",
|
and account = %s and ifnull(against_voucher, '') = ''""",
|
||||||
(against_voucher, account))[0][0])
|
(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
|
bal = against_voucher_amount + bal
|
||||||
if against_voucher_amount < 0:
|
if against_voucher_amount < 0:
|
||||||
bal = -bal
|
bal = -bal
|
||||||
|
@ -88,15 +88,24 @@ class JournalVoucher(AccountsController):
|
|||||||
def validate_against_jv(self):
|
def validate_against_jv(self):
|
||||||
for d in self.get('entries'):
|
for d in self.get('entries'):
|
||||||
if d.against_jv:
|
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:
|
if d.against_jv == self.name:
|
||||||
frappe.throw(_("You can not enter current voucher in 'Against Journal Voucher' column"))
|
frappe.throw(_("You can not enter current voucher in 'Against Journal Voucher' column"))
|
||||||
|
|
||||||
against_entries = frappe.db.sql("""select * from `tabJournal Voucher Detail`
|
against_entries = frappe.db.sql("""select * from `tabJournal Voucher Detail`
|
||||||
where account = %s and docstatus = 1 and parent = %s
|
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:
|
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))
|
.format(d.against_jv, d.account))
|
||||||
else:
|
else:
|
||||||
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
||||||
@ -153,7 +162,7 @@ class JournalVoucher(AccountsController):
|
|||||||
and voucher_account != d.account:
|
and voucher_account != d.account:
|
||||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
||||||
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
||||||
|
|
||||||
if against_field in ["against_sales_order", "against_purchase_order"]:
|
if against_field in ["against_sales_order", "against_purchase_order"]:
|
||||||
if voucher_account != account_master_name:
|
if voucher_account != account_master_name:
|
||||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} 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):
|
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
||||||
for voucher_no, payment_list in payment_against_voucher.items():
|
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"])
|
["docstatus", "outstanding_amount"])
|
||||||
|
|
||||||
if voucher_properties[0] != 1:
|
if voucher_properties[0] != 1:
|
||||||
@ -177,7 +186,7 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
||||||
for voucher_no, payment_list in payment_against_voucher.items():
|
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"])
|
["docstatus", "per_billed", "status", "advance_paid", "grand_total"])
|
||||||
|
|
||||||
if voucher_properties[0] != 1:
|
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))
|
(filters["account"], "%%%s%%" % txt, start, page_len))
|
||||||
|
|
||||||
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||||
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
|
return frappe.db.sql("""select distinct jv.name, jv.posting_date, jv.user_remark
|
||||||
from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jv_detail
|
from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jvd
|
||||||
where jv_detail.parent = jv.name and jv_detail.account = %s and jv.docstatus = 1
|
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""" %
|
and jv.%s like %s order by jv.name desc limit %s, %s""" %
|
||||||
("%s", searchfield, "%s", "%s", "%s"),
|
("%s", searchfield, "%s", "%s", "%s"),
|
||||||
(filters["account"], "%%%s%%" % txt, start, page_len))
|
(filters["account"], "%%%s%%" % txt, start, page_len))
|
||||||
|
@ -19,9 +19,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.frm.set_query('bank_cash_account', function() {
|
this.frm.set_query('bank_cash_account', function() {
|
||||||
if(!me.frm.doc.company) {
|
if(!me.frm.doc.company) {
|
||||||
msgprint(__("Please select company first"));
|
msgprint(__("Please select company first"));
|
||||||
@ -35,12 +35,8 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var help_content = '<i class="icon-hand-right"></i> ' + __("Note") + ':<br>'+
|
|
||||||
'<ul>' + __("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.") + '</ul>';
|
|
||||||
this.frm.set_value("reconcile_help", help_content);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get_unreconciled_entries: function() {
|
get_unreconciled_entries: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
return this.frm.call({
|
return this.frm.call({
|
||||||
@ -48,12 +44,12 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
method: 'get_unreconciled_entries',
|
method: 'get_unreconciled_entries',
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
var invoices = [];
|
var invoices = [];
|
||||||
|
|
||||||
$.each(me.frm.doc.payment_reconciliation_invoices || [], function(i, row) {
|
$.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);
|
invoices.push(row.invoice_number);
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
|
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
|
||||||
me.frm.doc.name).options = invoices.join("\n");
|
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}));
|
$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
|
||||||
|
|
||||||
cur_frm.add_fetch('party_account', 'master_type', 'party_type')
|
cur_frm.add_fetch('party_account', 'master_type', 'party_type')
|
||||||
|
@ -118,19 +118,12 @@
|
|||||||
"options": "Payment Reconciliation Invoice",
|
"options": "Payment Reconciliation Invoice",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "reconcile_help",
|
|
||||||
"fieldtype": "Small Text",
|
|
||||||
"label": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"read_only": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_toolbar": 1,
|
"hide_toolbar": 1,
|
||||||
"icon": "icon-resize-horizontal",
|
"icon": "icon-resize-horizontal",
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"modified": "2014-07-31 05:43:03.410832",
|
"modified": "2014-10-16 17:51:44.367107",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation",
|
"name": "Payment Reconciliation",
|
||||||
|
@ -124,7 +124,7 @@ class PaymentReconciliation(Document):
|
|||||||
dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
|
dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
|
||||||
lst = []
|
lst = []
|
||||||
for e in self.get('payment_reconciliation_payments'):
|
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({
|
lst.append({
|
||||||
'voucher_no' : e.journal_voucher,
|
'voucher_no' : e.journal_voucher,
|
||||||
'voucher_detail_no' : e.voucher_detail_number,
|
'voucher_detail_no' : e.voucher_detail_number,
|
||||||
@ -134,7 +134,7 @@ class PaymentReconciliation(Document):
|
|||||||
'is_advance' : e.is_advance,
|
'is_advance' : e.is_advance,
|
||||||
'dr_or_cr' : dr_or_cr,
|
'dr_or_cr' : dr_or_cr,
|
||||||
'unadjusted_amt' : flt(e.amount),
|
'unadjusted_amt' : flt(e.amount),
|
||||||
'allocated_amt' : flt(e.amount)
|
'allocated_amt' : flt(e.allocated_amount)
|
||||||
})
|
})
|
||||||
|
|
||||||
if lst:
|
if lst:
|
||||||
@ -162,18 +162,23 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
invoices_to_reconcile = []
|
invoices_to_reconcile = []
|
||||||
for p in self.get("payment_reconciliation_payments"):
|
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)
|
invoices_to_reconcile.append(p.invoice_number)
|
||||||
|
|
||||||
if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
|
if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
|
||||||
frappe.throw(_("{0}: {1} not found in Invoice Details table")
|
frappe.throw(_("{0}: {1} not found in Invoice Details table")
|
||||||
.format(p.invoice_type, p.invoice_number))
|
.format(p.invoice_type, p.invoice_number))
|
||||||
|
|
||||||
if p.amount > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
|
if flt(p.allocated_amount) > flt(p.amount):
|
||||||
frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount. Please refer Note below.").format(p.idx))
|
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:
|
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):
|
def check_condition(self, dr_or_cr):
|
||||||
cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or ""
|
cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or ""
|
||||||
|
@ -53,11 +53,20 @@
|
|||||||
"label": "Column Break",
|
"label": "Column Break",
|
||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "allocated_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Allocated amount",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"default": "Sales Invoice",
|
"default": "Sales Invoice",
|
||||||
"fieldname": "invoice_type",
|
"fieldname": "invoice_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 0,
|
||||||
"label": "Invoice Type",
|
"label": "Invoice Type",
|
||||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher",
|
"options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -95,7 +104,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2014-07-21 16:53:56.206169",
|
"modified": "2014-10-16 17:40:54.040194",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Payment",
|
"name": "Payment Reconciliation Payment",
|
||||||
|
@ -245,7 +245,7 @@ class DeliveryNote(SellingController):
|
|||||||
sl_entries = []
|
sl_entries = []
|
||||||
for d in self.get_item_list():
|
for d in self.get_item_list():
|
||||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
|
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)
|
self.update_reserved_qty(d)
|
||||||
|
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
|
@ -73,7 +73,7 @@ def get_reserved_qty(item_code, warehouse):
|
|||||||
from `tabPacked Item` dnpi_in
|
from `tabPacked Item` dnpi_in
|
||||||
where item_code = %s and warehouse = %s
|
where item_code = %s and warehouse = %s
|
||||||
and parenttype="Sales Order"
|
and parenttype="Sales Order"
|
||||||
and item_code != parent_item
|
and item_code != parent_item
|
||||||
and exists (select * from `tabSales Order` so
|
and exists (select * from `tabSales Order` so
|
||||||
where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped')
|
where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped')
|
||||||
) dnpi)
|
) dnpi)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user