Advance Payment Entry adjustment against Invoice

This commit is contained in:
Nabin Hait
2016-06-27 17:41:39 +05:30
parent e3a10170f7
commit 28a0528ae4
21 changed files with 509 additions and 278 deletions

View File

@@ -42,6 +42,7 @@ class AccountsController(TransactionBase):
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
self.validate_due_date()
self.validate_advance_entries()
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
@@ -276,22 +277,69 @@ class AccountsController(TransactionBase):
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
and allocated_amount = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
def get_advances(self, account_head, party_type, party, child_doctype, parentfield, dr_or_cr, against_order_field):
def set_advances(self):
"""Returns list of advances against Account, Party, Reference"""
order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
res = self.get_advance_entries()
# conver sales_order to "Sales Order"
reference_type = against_order_field.replace("_", " ").title()
self.set("advances", [])
for d in res:
self.append("advances", {
"doctype": self.doctype + " Advance",
"reference_type": d.reference_type,
"reference_name": d.reference_name,
"reference_row": d.reference_row,
"remarks": d.remarks,
"advance_amount": flt(d.amount),
"allocated_amount": flt(d.amount) if d.against_order else 0
})
def get_advance_entries(self, include_unallocated=True):
if self.doctype == "Sales Invoice":
party_account = self.debit_to
party_type = "Customer"
party = self.customer
amount_field = "credit_in_account_currency"
order_field = "sales_order"
order_doctype = "Sales Order"
else:
party_account = self.credit_to
party_type = "Supplier"
party = self.supplier
amount_field = "debit_in_account_currency"
order_field = "purchase_order"
order_doctype = "Purchase Order"
order_list = list(set([d.get(order_field)
for d in self.get("items") if d.get(order_field)]))
journal_entries = self.get_advance_journal_entries(party_type, party, party_account,
amount_field, order_doctype, order_list, include_unallocated)
payment_entries = self.get_advance_payment_entries(party_type, party, party_account,
order_doctype, order_list, include_unallocated)
res = journal_entries + payment_entries
return res
def get_advance_journal_entries(self, party_type, party, party_account, amount_field,
order_doctype, order_list, include_unallocated=True):
conditions = []
if include_unallocated:
conditions.append("ifnull(t2.reference_name, '')=''")
condition = ""
if order_list:
in_placeholder = ', '.join(['%s'] * len(order_list))
condition = "or (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\
.format(reference_type, in_placeholder)
res = frappe.db.sql("""
order_condition = ', '.join(['%s'] * len(order_list))
conditions.append(" (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\
.format(order_doctype, order_condition))
reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else ""
journal_entries = frappe.db.sql("""
select
t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no,
"Journal Entry" as reference_type, t1.name as reference_name,
t1.remark as remarks, t2.{0} as amount, t2.name as reference_row,
t2.reference_name as against_order
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
@@ -299,48 +347,98 @@ class AccountsController(TransactionBase):
t1.name = t2.parent and t2.account = %s
and t2.party_type = %s and t2.party = %s
and t2.is_advance = 'Yes' and t1.docstatus = 1
and (ifnull(t2.reference_type, '')='' {1})
order by t1.posting_date""".format(dr_or_cr, condition),
[account_head, party_type, party] + order_list, as_dict=1)
self.set(parentfield, [])
for d in res:
self.append(parentfield, {
"doctype": child_doctype,
"journal_entry": d.jv_no,
"jv_detail_no": d.jv_detail_no,
"remarks": d.remark,
"advance_amount": flt(d.amount),
"allocated_amount": flt(d.amount) if d.against_order else 0
})
def validate_advance_jv(self, reference_type):
against_order_field = frappe.scrub(reference_type)
order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
and (ifnull(t2.reference_name, '')='' {1})
order by t1.posting_date""".format(amount_field, reference_condition),
[party_account, party_type, party] + order_list, as_dict=1)
return list(journal_entries)
def get_advance_payment_entries(self, party_type, party, party_account,
order_doctype, order_list, include_unallocated=True):
party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
payment_type = "Receive" if party_type == "Customer" else "Pay"
payment_entries_against_order, unallocated_payment_entries = [], []
if order_list:
account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
payment_entries_against_order = frappe.db.sql("""
select
"Payment Entry" as reference_type, t1.name as reference_name,
t1.remarks, t2.allocated_amount as amount, t2.name as reference_row,
t2.reference_name as against_order
from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
where
t1.name = t2.parent and t1.{0} = %s and t1.payment_type = %s
and t1.party_type = %s and t1.party = %s and t1.docstatus = 1
and t2.reference_doctype = %s and t2.reference_name in ({1})
""".format(party_account_field, ', '.join(['%s'] * len(order_list))),
[party_account, payment_type, party_type, party, order_doctype] + order_list, as_dict=1)
if include_unallocated:
unallocated_payment_entries = frappe.db.sql("""
select "Payment Entry" as reference_type, name as reference_name,
remarks, unallocated_amount as amount
from `tabPayment Entry`
where
{0} = %s and party_type = %s and party = %s and payment_type = %s
and docstatus = 1 and unallocated_amount > 0
""".format(party_account_field), (party_account, party_type, party, payment_type), as_dict=1)
return list(payment_entries_against_order) + list(unallocated_payment_entries)
jv_against_order = frappe.db.sql("""select parent, reference_name as against_order
from `tabJournal Entry Account`
where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
and reference_type=%s
and ifnull(reference_name, '') in ({0})
group by parent, reference_name""".format(', '.join(['%s']*len(order_list))),
tuple([account, reference_type] + order_list), as_dict=1)
if jv_against_order:
order_jv_map = {}
for d in jv_against_order:
order_jv_map.setdefault(d.against_order, []).append(d.parent)
advance_jv_against_si = [d.journal_entry for d in self.get("advances")]
for order, jv_list in order_jv_map.items():
for jv in jv_list:
if not advance_jv_against_si or jv not in advance_jv_against_si:
frappe.msgprint(_("Journal Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
.format(jv, order))
def validate_advance_entries(self):
advance_entries = self.get_advance_entries(include_unallocated=False)
if advance_entries:
advance_entries_against_si = [d.reference_name for d in self.get("advances")]
for d in advance_entries:
if not advance_entries_against_si or d.reference_name not in advance_entries_against_si:
frappe.msgprint(_("Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
.format(d.reference_name, d.against_order))
def update_against_document_in_jv(self):
"""
Links invoice and advance voucher:
1. cancel advance voucher
2. split into multiple rows if partially adjusted, assign against voucher
3. submit advance voucher
"""
if self.doctype == "Sales Invoice":
party = self.customer
party_account = self.debit_to
dr_or_cr = "credit_in_account_currency"
else:
party = self.supplier
party_account = self.credit_to
dr_or_cr = "debit_in_account_currency"
lst = []
for d in self.get('advances'):
if flt(d.allocated_amount) > 0:
args = frappe._dict({
'voucher_type': d.reference_type,
'voucher_no' : d.reference_name,
'voucher_detail_no' : d.reference_row,
'against_voucher_type' : self.doctype,
'against_voucher' : self.name,
'account' : party_account,
'party_type': 'Customer',
'party': party,
'is_advance' : 'Yes',
'dr_or_cr' : dr_or_cr,
'unadjusted_amount' : flt(d.advance_amount),
'allocated_amount' : flt(d.allocated_amount),
'exchange_rate': (self.conversion_rate
if self.party_account_currency != self.company_currency else 1),
'grand_total': (self.base_grand_total
if self.party_account_currency==self.company_currency else self.grand_total),
'outstanding_amount': self.outstanding_amount
})
lst.append(args)
if lst:
from erpnext.accounts.utils import reconcile_against_document
reconcile_against_document(lst)
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_tolerance_for