def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None): ref_doc = frappe.get_doc(dt, dn) if dt == "Sales Invoice": party_type = "Customer" party_account = get_party_account_based_on_invoice_discounting(dn) or ref_doc.debit_to else: party_type = "Supplier" party_account = ref_doc.credit_to if (dt == "Sales Invoice" and ref_doc.outstanding_amount > 0) \ or (dt == "Purchase Invoice" and ref_doc.outstanding_amount < 0): amount_field_party = "credit_in_account_currency" amount_field_bank = "debit_in_account_currency" else: amount_field_party = "debit_in_account_currency" amount_field_bank = "credit_in_account_currency" return get_payment_entry(ref_doc, { "party_type": party_type, "party_account": party_account, "party_account_currency": ref_doc.party_account_currency, "amount_field_party": amount_field_party, "amount_field_bank": amount_field_bank, "amount": amount if amount else abs(ref_doc.outstanding_amount), "debit_in_account_currency": debit_in_account_currency, "remarks": 'Payment received against {0} {1}. {2}'.format(dt, dn, ref_doc.remarks), "is_advance": "No", "bank_account": bank_account, "journal_entry": journal_entry })
def validate_reference_documents(self): if self.party_type == "Student": valid_reference_doctypes = ("Fees") elif self.party_type == "Customer": valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry") elif self.party_type == "Supplier": valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") elif self.party_type == "Employee": valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance") elif self.party_type == "Shareholder": valid_reference_doctypes = ("Journal Entry") for d in self.get("references"): if not d.allocated_amount: continue if d.reference_doctype not in valid_reference_doctypes: frappe.throw(_("Reference Doctype must be one of {0}") .format(comma_or(valid_reference_doctypes))) elif d.reference_name: if not frappe.db.exists(d.reference_doctype, d.reference_name): frappe.throw(_("{0} {1} does not exist").format(d.reference_doctype, d.reference_name)) else: ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name) if d.reference_doctype != "Journal Entry": if self.party != ref_doc.get(scrub(self.party_type)): frappe.throw(_("{0} {1} is not associated with {2} {3}") .format(d.reference_doctype, d.reference_name, self.party_type, self.party)) else: self.validate_journal_entry() if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"): if self.party_type == "Customer": ref_party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to elif self.party_type == "Student": ref_party_account = ref_doc.receivable_account elif self.party_type=="Supplier": ref_party_account = ref_doc.credit_to elif self.party_type=="Employee": ref_party_account = ref_doc.payable_account if ref_party_account != self.party_account: frappe.throw(_("{0} {1} is associated with {2}, but Party Account is {3}") .format(d.reference_doctype, d.reference_name, ref_party_account, self.party_account)) if ref_doc.docstatus != 1: frappe.throw(_("{0} {1} must be submitted") .format(d.reference_doctype, d.reference_name))
def check_party_account(self): if self.reconciliation_doctype == "Sales Invoice": party_account = set([ get_party_account_based_on_invoice_discounting(doc.get("name")) or doc.get("debit_to") for doc in self.documents ]) elif self.reconciliation_doctype == "Purchase Invoice": party_account = set( [doc.get("credit_to") for doc in self.documents]) elif self.reconciliation_doctype == "Employee Advance": party_account = set( [doc.get("advance_account") for doc in self.documents]) elif self.reconciliation_doctype == "Expense Claim": party_account = set( [doc.get("payable_account") for doc in self.documents]) if not party_account or len(party_account) > 1: frappe.throw( _("Please select documents linked to the same party account")) else: self.party_account = next(iter(party_account))
def validate_reference_doc(self): """Validates reference document""" field_dict = { 'Sales Invoice': ["Customer", "Debit To"], 'Purchase Invoice': ["Supplier", "Credit To"], 'Sales Order': ["Customer"], 'Purchase Order': ["Supplier"] } self.reference_totals = {} self.reference_types = {} self.reference_accounts = {} for d in self.get("accounts"): if not d.reference_type: d.reference_name = None if not d.reference_name: d.reference_type = None if d.reference_type and d.reference_name and (d.reference_type in list(field_dict)): dr_or_cr = "credit_in_account_currency" \ if d.reference_type in ("Sales Order", "Sales Invoice") else "debit_in_account_currency" # check debit or credit type Sales / Purchase Order if d.reference_type=="Sales Order" and flt(d.debit) > 0: frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, d.reference_type)) if d.reference_type == "Purchase Order" and flt(d.credit) > 0: frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type)) # set totals if not d.reference_name in self.reference_totals: self.reference_totals[d.reference_name] = 0.0 if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'): self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) self.reference_types[d.reference_name] = d.reference_type self.reference_accounts[d.reference_name] = d.account against_voucher = frappe.db.get_value(d.reference_type, d.reference_name, [scrub(dt) for dt in field_dict.get(d.reference_type)]) if not against_voucher: frappe.throw(_("Row {0}: Invalid reference {1}").format(d.idx, d.reference_name)) # check if party and account match if d.reference_type in ("Sales Invoice", "Purchase Invoice"): if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no: debit_or_credit = 'Debit' if d.debit else 'Credit' party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no, debit_or_credit) else: if d.reference_type == "Sales Invoice": party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] else: party_account = against_voucher[1] if (against_voucher[0] != d.party or party_account != d.account): frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1], d.reference_type, d.reference_name)) # check if party matches for Sales / Purchase Order if d.reference_type in ("Sales Order", "Purchase Order"): # set totals if against_voucher != d.party: frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \ .format(d.idx, d.party_type, d.party, d.reference_type)) self.validate_orders() self.validate_invoices()
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): doc = frappe.get_doc(dt, dn) if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) if dt in ("Sales Invoice", "Sales Order"): party_type = "Customer" elif dt in ("Purchase Invoice", "Purchase Order"): party_type = "Supplier" elif dt in ("Expense Claim", "Employee Advance"): party_type = "Employee" elif dt in ("Fees"): party_type = "Student" # party account if dt == "Sales Invoice": party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to elif dt == "Purchase Invoice": party_account = doc.credit_to elif dt == "Fees": party_account = doc.receivable_account elif dt == "Employee Advance": party_account = doc.advance_account elif dt == "Expense Claim": party_account = doc.payable_account else: party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) if dt not in ("Sales Invoice", "Purchase Invoice"): party_account_currency = get_account_currency(party_account) else: party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) # payment type if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \ or (dt=="Purchase Invoice" and doc.outstanding_amount < 0): payment_type = "Receive" else: payment_type = "Pay" # amounts grand_total = outstanding_amount = 0 if party_amount: grand_total = outstanding_amount = party_amount elif dt in ("Sales Invoice", "Purchase Invoice"): if party_account_currency == doc.company_currency: grand_total = doc.base_rounded_total or doc.base_grand_total else: grand_total = doc.rounded_total or doc.grand_total outstanding_amount = doc.outstanding_amount elif dt in ("Expense Claim"): grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges outstanding_amount = doc.grand_total \ - doc.total_amount_reimbursed elif dt == "Employee Advance": grand_total = doc.advance_amount outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount) elif dt == "Fees": grand_total = doc.grand_total outstanding_amount = doc.outstanding_amount else: if party_account_currency == doc.company_currency: grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total) else: grand_total = flt(doc.get("rounded_total") or doc.grand_total) outstanding_amount = grand_total - flt(doc.advance_paid) # bank or cash bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account) if not bank: bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"), account=bank_account) paid_amount = received_amount = 0 if party_account_currency == bank.account_currency: paid_amount = received_amount = abs(outstanding_amount) elif payment_type == "Receive": paid_amount = abs(outstanding_amount) if bank_amount: received_amount = bank_amount else: received_amount = paid_amount * doc.conversion_rate else: received_amount = abs(outstanding_amount) if bank_amount: paid_amount = bank_amount else: # if party account currency and bank currency is different then populate paid amount as well paid_amount = received_amount * doc.conversion_rate pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type pe.company = doc.company pe.cost_center = doc.get("cost_center") pe.posting_date = nowdate() pe.mode_of_payment = doc.get("mode_of_payment") pe.party_type = party_type pe.party = doc.get(scrub(party_type)) pe.contact_person = doc.get("contact_person") pe.contact_email = doc.get("contact_email") pe.ensure_supplier_is_not_blocked() pe.paid_from = party_account if payment_type=="Receive" else bank.account pe.paid_to = party_account if payment_type=="Pay" else bank.account pe.paid_from_account_currency = party_account_currency \ if payment_type=="Receive" else bank.account_currency pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency pe.paid_amount = paid_amount pe.received_amount = received_amount pe.letter_head = doc.get("letter_head") if pe.party_type in ["Customer", "Supplier"]: bank_account = get_party_bank_account(pe.party_type, pe.party) pe.set("bank_account", bank_account) pe.set_bank_account_data() # only Purchase Invoice can be blocked individually if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked(): frappe.msgprint(_('{0} is on hold till {1}'.format(doc.name, doc.release_date))) else: if (doc.doctype in ('Sales Invoice', 'Purchase Invoice') and frappe.get_value('Payment Terms Template', {'name': doc.payment_terms_template}, 'allocate_payment_based_on_payment_terms')): for reference in get_reference_as_per_payment_terms(doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount): pe.append('references', reference) else: pe.append("references", { 'reference_doctype': dt, 'reference_name': dn, "bill_no": doc.get("bill_no"), "due_date": doc.get("due_date"), 'total_amount': grand_total, 'outstanding_amount': outstanding_amount, 'allocated_amount': outstanding_amount }) pe.setup_party_account_field() pe.set_missing_values() if party_account and bank: pe.set_exchange_rate() pe.set_amounts() return pe