payment to invoice matching
This commit is contained in:
parent
e5bd955f4d
commit
5b98340d15
@ -132,7 +132,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "difference",
|
"fieldname": "difference",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Difference",
|
"label": "Difference (Dr - Cr)",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "difference",
|
"oldfieldname": "difference",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
@ -440,7 +440,7 @@
|
|||||||
"icon": "icon-file-text",
|
"icon": "icon-file-text",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2014-04-29 14:55:19.872882",
|
"modified": "2014-05-01 11:24:52.313364",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Voucher",
|
"name": "Journal Voucher",
|
||||||
|
|||||||
@ -21,22 +21,21 @@ class JournalVoucher(AccountsController):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
if not self.is_opening:
|
if not self.is_opening:
|
||||||
self.is_opening='No'
|
self.is_opening='No'
|
||||||
|
|
||||||
self.clearance_date = None
|
self.clearance_date = None
|
||||||
|
|
||||||
super(JournalVoucher, self).validate_date_with_fiscal_year()
|
super(JournalVoucher, self).validate_date_with_fiscal_year()
|
||||||
|
|
||||||
self.validate_debit_credit()
|
|
||||||
self.validate_cheque_info()
|
self.validate_cheque_info()
|
||||||
self.validate_entries_for_advance()
|
self.validate_entries_for_advance()
|
||||||
|
self.validate_debit_and_credit()
|
||||||
self.validate_against_jv()
|
self.validate_against_jv()
|
||||||
|
self.validate_against_sales_invoice()
|
||||||
|
self.validate_against_purchase_invoice()
|
||||||
self.set_against_account()
|
self.set_against_account()
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_aging_date()
|
self.set_aging_date()
|
||||||
self.set_print_format_fields()
|
self.set_print_format_fields()
|
||||||
|
|
||||||
|
|
||||||
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()
|
||||||
@ -49,16 +48,6 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
self.make_gl_entries(1)
|
self.make_gl_entries(1)
|
||||||
|
|
||||||
def on_trash(self):
|
|
||||||
pass
|
|
||||||
#if self.amended_from:
|
|
||||||
# frappe.delete_doc("Journal Voucher", self.amended_from)
|
|
||||||
|
|
||||||
def validate_debit_credit(self):
|
|
||||||
for d in self.get('entries'):
|
|
||||||
if d.debit and d.credit:
|
|
||||||
msgprint(_("You cannot credit and debit same account at the same time."), raise_exception=1)
|
|
||||||
|
|
||||||
def validate_cheque_info(self):
|
def validate_cheque_info(self):
|
||||||
if self.voucher_type in ['Bank Voucher']:
|
if self.voucher_type in ['Bank Voucher']:
|
||||||
if not self.cheque_no or not self.cheque_date:
|
if not self.cheque_no or not self.cheque_date:
|
||||||
@ -81,33 +70,68 @@ class JournalVoucher(AccountsController):
|
|||||||
for d in self.get('entries'):
|
for d in self.get('entries'):
|
||||||
if d.against_jv:
|
if d.against_jv:
|
||||||
if d.against_jv == self.name:
|
if d.against_jv == self.name:
|
||||||
msgprint(_("You can not enter current voucher in 'Against Journal Voucher' column"), raise_exception=1)
|
frappe.throw(_("You can not enter current voucher in 'Against Journal Voucher' column"))
|
||||||
elif not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
|
||||||
where account = %s and docstatus = 1 and parent = %s""",
|
against_entries = frappe.db.sql("""select * from `tabJournal Voucher Detail`
|
||||||
(d.account, d.against_jv)):
|
where account = %s and docstatus = 1 and parent = %s
|
||||||
msgprint(_("Journal Voucher {0} does not have account {1}.").format(d.against_jv, d.account), raise_exception=1)
|
and ifnull(against_jv, '') = ''""", (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")
|
||||||
|
.format(d.against_jv, d.account))
|
||||||
|
else:
|
||||||
|
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
||||||
|
valid = False
|
||||||
|
for jvd in against_entries:
|
||||||
|
if flt(jvd[dr_or_cr]) > 0:
|
||||||
|
valid = True
|
||||||
|
if not valid:
|
||||||
|
frappe.throw(_("Against Journal Voucher {0} does not have any unmatched {1} entry")
|
||||||
|
.format(d.against_jv, dr_or_cr))
|
||||||
|
|
||||||
|
def validate_against_sales_invoice(self):
|
||||||
|
for d in self.get("entries"):
|
||||||
|
if d.against_invoice:
|
||||||
|
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):
|
||||||
|
for d in self.get("entries"):
|
||||||
|
if d.against_voucher:
|
||||||
|
if flt(d.credit) > 0:
|
||||||
|
frappe.throw(_("Row {0}: Credit entry can not be linked with a Purchase Invoice")
|
||||||
|
.format(d.idx))
|
||||||
|
if frappe.db.get_value("Purchase Invoice", d.against_voucher, "credit_to") != d.account:
|
||||||
|
frappe.throw(_("Row {0}: Account does not match with \
|
||||||
|
Purchase Invoice Credit To account").format(d.idx, d.account))
|
||||||
|
|
||||||
def set_against_account(self):
|
def set_against_account(self):
|
||||||
# Debit = Credit
|
accounts_debited, accounts_credited = [], []
|
||||||
debit, credit = 0.0, 0.0
|
for d in self.get("entries"):
|
||||||
debit_list, credit_list = [], []
|
if flt(d.debit > 0): accounts_debited.append(d.account)
|
||||||
for d in self.get('entries'):
|
if flt(d.credit) > 0: accounts_credited.append(d.account)
|
||||||
debit += flt(d.debit, 2)
|
|
||||||
credit += flt(d.credit, 2)
|
|
||||||
if flt(d.debit)>0 and (d.account not in debit_list): debit_list.append(d.account)
|
|
||||||
if flt(d.credit)>0 and (d.account not in credit_list): credit_list.append(d.account)
|
|
||||||
|
|
||||||
self.total_debit = debit
|
for d in self.get("entries"):
|
||||||
self.total_credit = credit
|
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
|
||||||
|
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
|
||||||
|
|
||||||
|
def validate_debit_and_credit(self):
|
||||||
|
self.total_debit, self.total_credit = 0, 0
|
||||||
|
|
||||||
|
for d in self.get("entries"):
|
||||||
|
if d.debit and d.credit:
|
||||||
|
frappe.throw(_("You cannot credit and debit same account at the same time"))
|
||||||
|
|
||||||
|
self.total_debit = flt(self.total_debit) + flt(d.debit)
|
||||||
|
self.total_credit = flt(self.total_credit) + flt(d.credit)
|
||||||
|
|
||||||
if abs(self.total_debit-self.total_credit) > 0.001:
|
if abs(self.total_debit-self.total_credit) > 0.001:
|
||||||
msgprint(_("Debit must equal Credit. The difference is {0}").format(self.total_debit-self.total_credit),
|
frappe.throw(_("Total Debit must be equal to Total Credit. The difference is {0}")
|
||||||
raise_exception=1)
|
.format(self.total_debit - self.total_credit))
|
||||||
|
|
||||||
# update against account
|
|
||||||
for d in self.get('entries'):
|
|
||||||
if flt(d.debit) > 0: d.against_account = ', '.join(credit_list)
|
|
||||||
if flt(d.credit) > 0: d.against_account = ', '.join(debit_list)
|
|
||||||
|
|
||||||
def create_remarks(self):
|
def create_remarks(self):
|
||||||
r = []
|
r = []
|
||||||
@ -220,22 +244,9 @@ class JournalVoucher(AccountsController):
|
|||||||
|
|
||||||
return self.is_approving_authority
|
return self.is_approving_authority
|
||||||
|
|
||||||
def check_account_against_entries(self):
|
|
||||||
for d in self.get("entries"):
|
|
||||||
if d.against_invoice and frappe.db.get_value("Sales Invoice",
|
|
||||||
d.against_invoice, "debit_to") != d.account:
|
|
||||||
frappe.throw(_("Account {0} must be sames as Debit To Account in Sales Invoice in row {0}").format(d.account, d.idx))
|
|
||||||
|
|
||||||
if d.against_voucher and frappe.db.get_value("Purchase Invoice",
|
|
||||||
d.against_voucher, "credit_to") != d.account:
|
|
||||||
frappe.throw(_("Account {0} must be sames as Credit To Account in Purchase Invoice in row {0}").format(d.account, d.idx))
|
|
||||||
|
|
||||||
def make_gl_entries(self, cancel=0, adv_adj=0):
|
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
|
||||||
if not cancel:
|
|
||||||
self.check_account_against_entries()
|
|
||||||
|
|
||||||
gl_map = []
|
gl_map = []
|
||||||
for d in self.get("entries"):
|
for d in self.get("entries"):
|
||||||
if d.debit or d.credit:
|
if d.debit or d.credit:
|
||||||
|
|||||||
@ -15,25 +15,21 @@ class PaymenttoInvoiceMatchingTool(Document):
|
|||||||
total_amount = frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
|
total_amount = frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where voucher_type = %s and voucher_no = %s
|
where voucher_type = %s and voucher_no = %s
|
||||||
and account = %s""", (self.voucher_type, self.voucher_no, self.account))
|
and account = %s and ifnull(against_voucher, '') != voucher_no""",
|
||||||
|
(self.voucher_type, self.voucher_no, self.account))
|
||||||
|
|
||||||
self.total_amount = total_amount and flt(total_amount[0][0]) or 0
|
self.total_amount = total_amount and flt(total_amount[0][0]) or 0
|
||||||
|
|
||||||
reconciled_payment = frappe.db.sql("""
|
reconciled_payment = frappe.db.sql("""
|
||||||
select abs(sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)))
|
select abs(sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)))
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where against_voucher = %s and account = %s
|
where against_voucher_type = %s and against_voucher = %s and account = %s
|
||||||
""", (self.voucher_no, self.account))
|
""", (self.voucher_type, self.voucher_no, self.account))
|
||||||
|
|
||||||
reconciled_payment = reconciled_payment and flt(reconciled_payment[0][0]) or 0
|
reconciled_payment = reconciled_payment and flt(reconciled_payment[0][0]) or 0
|
||||||
self.unmatched_amount = self.total_amount - reconciled_payment
|
self.unmatched_amount = self.total_amount - reconciled_payment
|
||||||
|
|
||||||
def get_against_entries(self):
|
def get_against_entries(self):
|
||||||
"""
|
|
||||||
Get payment entries for the account and period
|
|
||||||
Payment entry will be decided based on account type (Dr/Cr)
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.set('against_entries', [])
|
self.set('against_entries', [])
|
||||||
gle = self.get_gl_entries()
|
gle = self.get_gl_entries()
|
||||||
self.create_against_entries_table(gle)
|
self.create_against_entries_table(gle)
|
||||||
@ -56,14 +52,16 @@ class PaymenttoInvoiceMatchingTool(Document):
|
|||||||
t1.name as voucher_no, t1.posting_date, t1.total_debit as total_amt,
|
t1.name as voucher_no, t1.posting_date, t1.total_debit as total_amt,
|
||||||
abs(ifnull(t2.debit, 0) - ifnull(t2.credit, 0)) as unmatched_amount, t1.remark,
|
abs(ifnull(t2.debit, 0) - ifnull(t2.credit, 0)) as unmatched_amount, t1.remark,
|
||||||
t2.against_account, t2.name as voucher_detail_no, t2.is_advance
|
t2.against_account, t2.name as voucher_detail_no, t2.is_advance
|
||||||
from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
from
|
||||||
where t1.name = t2.parent and t1.docstatus = 1 and t2.account = %s
|
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
||||||
|
where
|
||||||
|
t1.name = t2.parent and t1.docstatus = 1 and t2.account = %s
|
||||||
and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')=''
|
and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')=''
|
||||||
and ifnull(t2.against_jv, '')='' and t2.%s > 0 and t1.name != %s
|
and ifnull(t2.against_jv, '')='' and t2.%s > 0 and t1.name != %s
|
||||||
and not exists (select * from `tabJournal Voucher Detail`
|
and not exists (select * from `tabJournal Voucher Detail`
|
||||||
where parent=%s and against_jv = t1.name) %s
|
where parent=%s and against_jv = t1.name) %s
|
||||||
group by t1.name, t2.name """ %
|
group by t1.name, t2.name """ % ('%s', dr_or_cr, '%s', '%s', cond),
|
||||||
('%s', dr_or_cr, '%s', '%s', cond), (self.account, self.voucher_no, self.voucher_no), as_dict=1)
|
(self.account, self.voucher_no, self.voucher_no), as_dict=1)
|
||||||
|
|
||||||
return gle
|
return gle
|
||||||
|
|
||||||
@ -112,23 +110,15 @@ class PaymenttoInvoiceMatchingTool(Document):
|
|||||||
frappe.throw(_("Voucher No is not valid"))
|
frappe.throw(_("Voucher No is not valid"))
|
||||||
|
|
||||||
def reconcile(self):
|
def reconcile(self):
|
||||||
"""
|
|
||||||
Links booking and payment voucher
|
|
||||||
1. cancel payment voucher
|
|
||||||
2. split into multiple rows if partially adjusted, assign against voucher
|
|
||||||
3. submit payment voucher
|
|
||||||
"""
|
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
|
self.validate_allocated_amount()
|
||||||
if not self.total_allocated_amount:
|
|
||||||
frappe.throw(_("You must allocate amount before reconcile"))
|
|
||||||
|
|
||||||
dr_or_cr = "credit" if self.total_amount > 0 else "debit"
|
dr_or_cr = "credit" if self.total_amount > 0 else "debit"
|
||||||
|
|
||||||
lst = []
|
lst = []
|
||||||
for d in self.get('against_entries'):
|
for d in self.get('against_entries'):
|
||||||
if flt(d.allocated_amount) > 0:
|
if flt(d.allocated_amount) > 0:
|
||||||
args = {
|
lst.append({
|
||||||
'voucher_no' : d.voucher_no,
|
'voucher_no' : d.voucher_no,
|
||||||
'voucher_detail_no' : d.voucher_detail_no,
|
'voucher_detail_no' : d.voucher_detail_no,
|
||||||
'against_voucher_type' : self.voucher_type,
|
'against_voucher_type' : self.voucher_type,
|
||||||
@ -138,16 +128,21 @@ class PaymenttoInvoiceMatchingTool(Document):
|
|||||||
'dr_or_cr' : dr_or_cr,
|
'dr_or_cr' : dr_or_cr,
|
||||||
'unadjusted_amt' : flt(d.original_amount),
|
'unadjusted_amt' : flt(d.original_amount),
|
||||||
'allocated_amt' : flt(d.allocated_amount)
|
'allocated_amt' : flt(d.allocated_amount)
|
||||||
}
|
})
|
||||||
|
|
||||||
lst.append(args)
|
|
||||||
|
|
||||||
if lst:
|
if lst:
|
||||||
from erpnext.accounts.utils import reconcile_against_document
|
from erpnext.accounts.utils import reconcile_against_document
|
||||||
reconcile_against_document(lst)
|
reconcile_against_document(lst)
|
||||||
|
self.get_voucher_details()
|
||||||
self.get_against_entries()
|
self.get_against_entries()
|
||||||
msgprint(_("Successfully allocated"))
|
msgprint(_("Successfully allocated"))
|
||||||
|
|
||||||
|
def validate_allocated_amount(self):
|
||||||
|
if not self.total_allocated_amount:
|
||||||
|
frappe.throw(_("You must allocate amount before reconcile"))
|
||||||
|
elif self.total_allocated_amount > self.unmatched_amount:
|
||||||
|
frappe.throw(_("Total Allocated Amount can not be greater than unmatched amount"))
|
||||||
|
|
||||||
def get_voucher_nos(doctype, txt, searchfield, start, page_len, filters):
|
def get_voucher_nos(doctype, txt, searchfield, start, page_len, filters):
|
||||||
non_reconclied_entries = []
|
non_reconclied_entries = []
|
||||||
entries = frappe.db.sql("""
|
entries = frappe.db.sql("""
|
||||||
@ -172,7 +167,7 @@ def get_voucher_nos(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
""", (filters["account"], filters["voucher_type"], d.voucher_no))
|
""", (filters["account"], filters["voucher_type"], d.voucher_no))
|
||||||
adjusted_amount = adjusted_amount[0][0] if adjusted_amount else 0
|
adjusted_amount = adjusted_amount[0][0] if adjusted_amount else 0
|
||||||
|
|
||||||
if adjusted_amount != d.amount:
|
if d.amount > adjusted_amount:
|
||||||
non_reconclied_entries.append([d.voucher_no, d.posting_date, d.amount])
|
non_reconclied_entries.append([d.voucher_no, d.posting_date, d.amount])
|
||||||
|
|
||||||
return non_reconclied_entries
|
return non_reconclied_entries
|
||||||
|
|||||||
@ -369,14 +369,17 @@ 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):
|
||||||
res = frappe.db.sql("""select t1.name as jv_no, t1.remark,
|
res = frappe.db.sql("""
|
||||||
t2.%s as amount, t2.name as jv_detail_no
|
select
|
||||||
from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no
|
||||||
where t1.name = t2.parent and t2.account = %s and t2.is_advance = 'Yes'
|
from
|
||||||
and (t2.against_voucher is null or t2.against_voucher = '')
|
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
||||||
and (t2.against_invoice is null or t2.against_invoice = '')
|
where
|
||||||
and (t2.against_jv is null or t2.against_jv = '')
|
t1.name = t2.parent and t2.account = %s and t2.is_advance = 'Yes' and t1.docstatus = 1
|
||||||
and t1.docstatus = 1 order by t1.posting_date""" %
|
and ifnull(t2.against_voucher, '') = ''
|
||||||
|
and ifnull(t2.against_invoice, '') = ''
|
||||||
|
and ifnull(t2.against_jv, '') = ''
|
||||||
|
order by t1.posting_date""" %
|
||||||
(dr_or_cr, '%s'), account_head, as_dict=1)
|
(dr_or_cr, '%s'), account_head, as_dict=1)
|
||||||
|
|
||||||
self.set(parentfield, [])
|
self.set(parentfield, [])
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user