From 38d4be832556f2aff861034e29a466de98336ff4 Mon Sep 17 00:00:00 2001 From: Ben Cornwell-Mott Date: Mon, 17 Jul 2017 11:18:28 -0700 Subject: [PATCH] Update manuals. Fix codacy issues --- .../doctype/payment_entry/payment_entry.py | 268 +++++++++--------- .../payment_entry/test_payment_entry.py | 98 +++---- .../user/manual/en/accounts/payment-entry.md | 5 +- .../en/human-resources/expense-claim.md | 11 +- .../expense_claim/test_expense_claim.py | 14 +- 5 files changed, 202 insertions(+), 194 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 59a899b70d..7bb9a52ee6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -18,21 +18,21 @@ from erpnext.controllers.accounts_controller import AccountsController class InvalidPaymentEntry(ValidationError): pass class PaymentEntry(AccountsController): - def setup_party_account_field(self): + def setup_party_account_field(self): self.party_account_field = None self.party_account = None self.party_account_currency = None - + if self.payment_type == "Receive": self.party_account_field = "paid_from" self.party_account = self.paid_from self.party_account_currency = self.paid_from_account_currency - + elif self.payment_type == "Pay": self.party_account_field = "paid_to" self.party_account = self.paid_to self.party_account_currency = self.paid_to_account_currency - + def validate(self): self.setup_party_account_field() self.set_missing_values() @@ -50,7 +50,7 @@ class PaymentEntry(AccountsController): self.set_remarks() self.validate_duplicate_entry() self.validate_allocated_amount() - + def on_submit(self): self.setup_party_account_field() if self.difference_amount: @@ -58,7 +58,7 @@ class PaymentEntry(AccountsController): self.make_gl_entries() self.update_advance_paid() self.update_expense_claim() - + def on_cancel(self): self.setup_party_account_field() self.make_gl_entries(cancel=1) @@ -72,8 +72,8 @@ class PaymentEntry(AccountsController): if (d.reference_doctype, d.reference_name) in reference_names: frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}").format(d.idx, d.reference_doctype, d.reference_name)) reference_names.append((d.reference_doctype, d.reference_name)) - - + + def validate_allocated_amount(self): for d in self.get("references"): if (flt(d.allocated_amount))> 0: @@ -89,98 +89,98 @@ class PaymentEntry(AccountsController): def set_missing_values(self): if self.payment_type == "Internal Transfer": - for field in ("party", "party_balance", "total_allocated_amount", + for field in ("party", "party_balance", "total_allocated_amount", "base_total_allocated_amount", "unallocated_amount"): self.set(field, None) self.references = [] else: if not self.party_type: frappe.throw(_("Party Type is mandatory")) - + if not self.party: frappe.throw(_("Party is mandatory")) - - self.party_name = frappe.db.get_value(self.party_type, self.party, + + self.party_name = frappe.db.get_value(self.party_type, self.party, self.party_type.lower() + "_name") - + if self.party: if not self.party_balance: self.party_balance = get_balance_on(party_type=self.party_type, party=self.party, date=self.posting_date, company=self.company) - + if not self.party_account: party_account = get_party_account(self.party_type, self.party, self.company) self.set(self.party_account_field, party_account) self.party_account = party_account - + if self.paid_from and not (self.paid_from_account_currency or self.paid_from_account_balance): acc = get_account_details(self.paid_from, self.posting_date) self.paid_from_account_currency = acc.account_currency self.paid_from_account_balance = acc.account_balance - + if self.paid_to and not (self.paid_to_account_currency or self.paid_to_account_balance): acc = get_account_details(self.paid_to, self.posting_date) self.paid_to_account_currency = acc.account_currency self.paid_to_account_balance = acc.account_balance - + self.party_account_currency = self.paid_from_account_currency \ if self.payment_type=="Receive" else self.paid_to_account_currency - + self.set_missing_ref_details() - - + + def set_missing_ref_details(self): for d in self.get("references"): if d.allocated_amount: - ref_details = get_reference_details(d.reference_doctype, + ref_details = get_reference_details(d.reference_doctype, d.reference_name, self.party_account_currency) - + for field, value in ref_details.items(): if not d.get(field): d.set(field, value) - + def validate_payment_type(self): if self.payment_type not in ("Receive", "Pay", "Internal Transfer"): frappe.throw(_("Payment Type must be one of Receive, Pay and Internal Transfer")) - + def validate_party_details(self): if self.party: if not frappe.db.exists(self.party_type, self.party): frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party)) - + if self.party_account: party_account_type = "Receivable" if self.party_type=="Customer" else "Payable" self.validate_account_type(self.party_account, [party_account_type]) - + def validate_bank_accounts(self): if self.payment_type in ("Pay", "Internal Transfer"): self.validate_account_type(self.paid_from, ["Bank", "Cash"]) - + if self.payment_type in ("Receive", "Internal Transfer"): self.validate_account_type(self.paid_to, ["Bank", "Cash"]) - + def validate_account_type(self, account, account_types): account_type = frappe.db.get_value("Account", account, "account_type") if account_type not in account_types: frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types))) - + def set_exchange_rate(self): if self.paid_from and not self.source_exchange_rate: if self.paid_from_account_currency == self.company_currency: self.source_exchange_rate = 1 else: - self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency, + self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency, self.company_currency, self.posting_date) - + if self.paid_to and not self.target_exchange_rate: - self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency, + self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency, self.company_currency, self.posting_date) - + def validate_mandatory(self): for field in ("paid_amount", "received_amount", "source_exchange_rate", "target_exchange_rate"): if not self.get(field): frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field))) - + def validate_reference_documents(self): if self.party_type == "Customer": valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry") @@ -195,7 +195,7 @@ class PaymentEntry(AccountsController): 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)) @@ -208,7 +208,7 @@ class PaymentEntry(AccountsController): .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"): if self.party_type=="Customer": ref_party_account = ref_doc.debit_to @@ -216,18 +216,18 @@ class PaymentEntry(AccountsController): 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 validate_journal_entry(self): for d in self.get("references"): - if d.allocated_amount and d.reference_doctype == "Journal Entry": + if d.allocated_amount and d.reference_doctype == "Journal Entry": je_accounts = frappe.db.sql("""select debit, credit from `tabJournal Entry Account` where account = %s and party=%s and docstatus = 1 and parent = %s and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order")) @@ -245,7 +245,7 @@ class PaymentEntry(AccountsController): if not valid: frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry") .format(d.reference_name, dr_or_cr)) - + def set_amounts(self): self.set_amounts_in_company_currency() self.set_total_allocated_amount() @@ -255,111 +255,111 @@ class PaymentEntry(AccountsController): def set_amounts_in_company_currency(self): self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0 if self.paid_amount: - self.base_paid_amount = flt(flt(self.paid_amount) * flt(self.source_exchange_rate), + self.base_paid_amount = flt(flt(self.paid_amount) * flt(self.source_exchange_rate), self.precision("base_paid_amount")) - + if self.received_amount: - self.base_received_amount = flt(flt(self.received_amount) * flt(self.target_exchange_rate), + self.base_received_amount = flt(flt(self.received_amount) * flt(self.target_exchange_rate), self.precision("base_received_amount")) - + def set_total_allocated_amount(self): if self.payment_type == "Internal Transfer": return - + total_allocated_amount, base_total_allocated_amount = 0, 0 for d in self.get("references"): - if d.allocated_amount: + if d.allocated_amount: total_allocated_amount += flt(d.allocated_amount) - base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate), + base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")) - + self.total_allocated_amount = abs(total_allocated_amount) self.base_total_allocated_amount = abs(base_total_allocated_amount) - + def set_unallocated_amount(self): self.unallocated_amount = 0; if self.party: party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount - + total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) - + if self.total_allocated_amount < party_amount: if self.payment_type == "Receive": self.unallocated_amount = party_amount - (self.total_allocated_amount - total_deductions) else: self.unallocated_amount = party_amount - (self.total_allocated_amount + total_deductions) - + def set_difference_amount(self): - base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate) + base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate) if self.payment_type=="Receive" else flt(self.target_exchange_rate)) - + base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount) - + if self.payment_type == "Receive": self.difference_amount = base_party_amount - self.base_received_amount elif self.payment_type == "Pay": self.difference_amount = self.base_paid_amount - base_party_amount else: self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) - + for d in self.get("deductions"): if d.amount: self.difference_amount -= flt(d.amount) - + self.difference_amount = flt(self.difference_amount, self.precision("difference_amount")) - + def clear_unallocated_reference_document_rows(self): self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]})) - frappe.db.sql("""delete from `tabPayment Entry Reference` + frappe.db.sql("""delete from `tabPayment Entry Reference` where parent = %s and allocated_amount = 0""", self.name) - + def validate_payment_against_negative_invoice(self): - if ((self.payment_type=="Pay" and self.party_type=="Customer") + if ((self.payment_type=="Pay" and self.party_type=="Customer") or (self.payment_type=="Receive" and self.party_type=="Supplier")): - - total_negative_outstanding = sum([abs(flt(d.outstanding_amount)) + + total_negative_outstanding = sum([abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0]) - + party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount if not total_negative_outstanding: frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice") - .format(self.payment_type, ("to" if self.party_type=="Customer" else "from"), + .format(self.payment_type, ("to" if self.party_type=="Customer" else "from"), self.party_type), InvalidPaymentEntry) - + elif party_amount > total_negative_outstanding: frappe.throw(_("Paid Amount cannot be greater than total negative outstanding amount {0}") .format(total_negative_outstanding), InvalidPaymentEntry) - + def set_title(self): if self.payment_type in ("Receive", "Pay"): self.title = self.party else: self.title = self.paid_from + " - " + self.paid_to - + def validate_transaction_reference(self): bank_account = self.paid_to if self.payment_type == "Receive" else self.paid_from bank_account_type = frappe.db.get_value("Account", bank_account, "account_type") - + if bank_account_type == "Bank": if not self.reference_no or not self.reference_date: frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) - + def set_remarks(self): if self.remarks: return - + if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] else: - + remarks = [_("Amount {0} {1} {2} {3}").format( self.party_account_currency, self.paid_amount if self.payment_type=="Receive" else self.received_amount, _("received from") if self.payment_type=="Receive" else _("to"), self.party )] - + if self.reference_no: remarks.append(_("Transaction reference no {0} dated {1}") .format(self.reference_no, self.reference_date)) @@ -367,35 +367,35 @@ class PaymentEntry(AccountsController): if self.payment_type in ["Receive", "Pay"]: for d in self.get("references"): if d.allocated_amount: - remarks.append(_("Amount {0} {1} against {2} {3}").format(self.party_account_currency, + remarks.append(_("Amount {0} {1} against {2} {3}").format(self.party_account_currency, d.allocated_amount, d.reference_doctype, d.reference_name)) - + for d in self.get("deductions"): if d.amount: remarks.append(_("Amount {0} {1} deducted against {2}") .format(self.company_currency, d.amount, d.account)) self.set("remarks", "\n".join(remarks)) - + def make_gl_entries(self, cancel=0, adv_adj=0): if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): self.setup_party_account_field() - + gl_entries = [] self.add_party_gl_entries(gl_entries) self.add_bank_gl_entries(gl_entries) self.add_deductions_gl_entries(gl_entries) make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj) - + def add_party_gl_entries(self, gl_entries): if self.party_account: if self.payment_type=="Receive": against_account = self.paid_to else: against_account = self.paid_from - - + + party_gl_dict = self.get_gl_dict({ "account": self.party_account, "party_type": self.party_type, @@ -403,39 +403,39 @@ class PaymentEntry(AccountsController): "against": against_account, "account_currency": self.party_account_currency }) - + dr_or_cr = "credit" if self.party_type == "Customer" else "debit" - + for d in self.get("references"): gle = party_gl_dict.copy() gle.update({ "against_voucher_type": d.reference_doctype, "against_voucher": d.reference_name }) - - allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate), - self.precision("paid_amount")) - + + allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate), + self.precision("paid_amount")) + gle.update({ dr_or_cr + "_in_account_currency": d.allocated_amount, dr_or_cr: allocated_amount_in_company_currency }) - + gl_entries.append(gle) - + if self.unallocated_amount: base_unallocated_amount = base_unallocated_amount = self.unallocated_amount * \ (self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate) - + gle = party_gl_dict.copy() - + gle.update({ dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount }) gl_entries.append(gle) - + def add_bank_gl_entries(self, gl_entries): if self.payment_type in ("Pay", "Internal Transfer"): gl_entries.append( @@ -457,14 +457,14 @@ class PaymentEntry(AccountsController): "debit": self.base_received_amount }) ) - + def add_deductions_gl_entries(self, gl_entries): for d in self.get("deductions"): if d.amount: account_currency = get_account_currency(d.account) if account_currency != self.company_currency: frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency)) - + gl_entries.append( self.get_gl_dict({ "account": d.account, @@ -475,7 +475,7 @@ class PaymentEntry(AccountsController): "cost_center": d.cost_center }) ) - + def update_advance_paid(self): if self.payment_type in ("Receive", "Pay") and self.party: for d in self.get("references"): @@ -495,17 +495,17 @@ def get_outstanding_reference_documents(args): party_account_currency = get_account_currency(args.get("party_account")) company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency") - + # Get negative outstanding sales /purchase invoices total_field = "base_grand_total" if party_account_currency == company_currency else "grand_total" - - negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), + + negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"), args.get("party_account"), total_field) # Get positive outstanding sales /purchase invoices - outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), + outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), args.get("party_account")) - + for d in outstanding_invoices: d["exchange_rate"] = 1 if party_account_currency != company_currency: @@ -517,11 +517,11 @@ def get_outstanding_reference_documents(args): ) # Get all SO / PO which are not fully billed or aginst which full advance not paid - orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), args.get("party"), + orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), args.get("party"), party_account_currency, company_currency) - + return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed - + def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency): if party_type == "Customer": voucher_type = 'Sales Order' @@ -529,7 +529,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre voucher_type = 'Purchase Order' elif party_type == "Employee": voucher_type = None - + orders = [] if voucher_type: ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total" @@ -560,19 +560,19 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre for d in orders: d["voucher_type"] = voucher_type # This assumes that the exchange rate required is the one in the SO - d["exchange_rate"] = get_exchange_rate(party_account_currency, + d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date) order_list.append(d) return order_list - + def get_negative_outstanding_invoices(party_type, party, party_account, total_field): if party_type != "Employee": voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" return frappe.db.sql(""" select - "{voucher_type}" as voucher_type, name as voucher_no, - {total_field} as invoice_amount, outstanding_amount, posting_date, + "{voucher_type}" as voucher_type, name as voucher_no, + {total_field} as invoice_amount, outstanding_amount, posting_date, due_date, conversion_rate as exchange_rate from `tab{voucher_type}` @@ -588,18 +588,18 @@ def get_negative_outstanding_invoices(party_type, party, party_account, total_fi }), (party, party_account), as_dict = True) else: return [] - + @frappe.whitelist() def get_party_details(company, party_type, party, date): if not frappe.db.exists(party_type, party): frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) - + party_account = get_party_account(party_type, party, company) - + account_currency = get_account_currency(party_account) account_balance = get_balance_on(party_account, date) party_balance = get_balance_on(party_type=party_type, party=party) - + return { "party_account": party_account, "party_account_currency": account_currency, @@ -607,7 +607,7 @@ def get_party_details(company, party_type, party, date): "account_balance": account_balance } -@frappe.whitelist() +@frappe.whitelist() def get_account_details(account, date): frappe.has_permission('Payment Entry', throw=True) return frappe._dict({ @@ -615,61 +615,61 @@ def get_account_details(account, date): "account_balance": get_balance_on(account, date), "account_type": frappe.db.get_value("Account", account, "account_type") }) - + @frappe.whitelist() def get_company_defaults(company): fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"] ret = frappe.db.get_value("Company", company, fields, as_dict=1) - + for fieldname in fields: if not ret[fieldname]: frappe.throw(_("Please set default {0} in Company {1}") .format(frappe.get_meta("Company").get_label(fieldname), company)) - + return ret @frappe.whitelist() def get_reference_details(reference_doctype, reference_name, party_account_currency): total_amount = outstanding_amount = exchange_rate = None ref_doc = frappe.get_doc(reference_doctype, reference_name) - + if reference_doctype != "Journal Entry": if party_account_currency == ref_doc.company_currency: if ref_doc.doctype == "Expense Claim": total_amount = ref_doc.total_sanctioned_amount - else: + else: total_amount = ref_doc.base_grand_total exchange_rate = 1 else: total_amount = ref_doc.grand_total - - # Get the exchange rate from the original ref doc + + # Get the exchange rate from the original ref doc # or get it based on the posting date of the ref doc exchange_rate = ref_doc.get("conversion_rate") or \ get_exchange_rate(party_account_currency, ref_doc.company_currency, ref_doc.posting_date) - + outstanding_amount = ref_doc.get("outstanding_amount") \ if reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim") \ - else flt(total_amount) - flt(ref_doc.advance_paid) + else flt(total_amount) - flt(ref_doc.advance_paid) else: # Get the exchange rate based on the posting date of the ref doc - exchange_rate = get_exchange_rate(party_account_currency, + exchange_rate = get_exchange_rate(party_account_currency, ref_doc.company_currency, ref_doc.posting_date) - + return frappe._dict({ "due_date": ref_doc.get("due_date"), "total_amount": total_amount, "outstanding_amount": outstanding_amount, "exchange_rate": exchange_rate }) - + @frappe.whitelist() 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"): @@ -684,16 +684,16 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_account = doc.credit_to else: party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) - + party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) - + # payment type if (dt == "Sales Order" or (dt=="Sales Invoice" 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: @@ -710,9 +710,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= 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"), + bank = get_default_bank_cash_account(doc.company, "Bank", 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) @@ -724,7 +724,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= received_amount = abs(outstanding_amount) if bank_amount: paid_amount = bank_amount - + pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type pe.company = doc.company @@ -741,7 +741,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.received_amount = received_amount pe.allocate_payment_amount = 1 pe.letter_head = doc.get("letter_head") - + pe.append("references", { "reference_doctype": dt, "reference_name": dn, diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 9d6b75b1a7..0316ccafe2 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -21,102 +21,102 @@ class TestPaymentEntry(unittest.TestCase): pe.paid_from = "Debtors - _TC" pe.insert() pe.submit() - + expected_gle = dict((d[0], d) for d in [ ["Debtors - _TC", 0, 1000, so.name], ["_Test Cash - _TC", 1000.0, 0, None] ]) - + self.validate_gl_entries(pe.name, expected_gle) - + so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid") self.assertEqual(so_advance_paid, 1000) - + pe.cancel() - + self.assertFalse(self.get_gle(pe.name)) - + so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid") self.assertEqual(so_advance_paid, 0) - + def test_payment_entry_against_si_usd_to_usd(self): si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", currency="USD", conversion_rate=50) pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.target_exchange_rate = 50 + pe.target_exchange_rate = 50 pe.insert() pe.submit() - + expected_gle = dict((d[0], d) for d in [ ["_Test Receivable USD - _TC", 0, 5000, si.name], ["_Test Bank USD - _TC", 5000.0, 0, None] ]) - + self.validate_gl_entries(pe.name, expected_gle) - + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 0) - + pe.cancel() self.assertFalse(self.get_gle(pe.name)) - + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 100) - + def test_payment_entry_against_pi(self): pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", currency="USD", conversion_rate=50) pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC") pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.source_exchange_rate = 50 + pe.source_exchange_rate = 50 pe.insert() pe.submit() - + expected_gle = dict((d[0], d) for d in [ ["_Test Payable USD - _TC", 12500, 0, pi.name], ["_Test Bank USD - _TC", 0, 12500, None] ]) - + self.validate_gl_entries(pe.name, expected_gle) - + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 0) def test_payment_entry_against_ec(self): - + payable = frappe.db.get_value('Company', "_Test Company", 'default_payable_account') ec = make_expense_claim(payable, 300, 300, "_Test Company","Travel Expenses - _TC") pe = get_payment_entry("Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300) pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.source_exchange_rate = 1 + pe.source_exchange_rate = 1 pe.insert() pe.submit() - + expected_gle = dict((d[0], d) for d in [ [payable, 300, 0, ec.name], ["_Test Bank USD - _TC", 0, 300, None] ]) - + self.validate_gl_entries(pe.name, expected_gle) - + outstanding_amount = flt(frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")) - \ flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed")) self.assertEqual(outstanding_amount, 0) - + def test_payment_entry_against_si_usd_to_inr(self): si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", currency="USD", conversion_rate=50) - pe = get_payment_entry("Sales Invoice", si.name, party_amount=20, + pe = get_payment_entry("Sales Invoice", si.name, party_amount=20, bank_account="_Test Bank - _TC", bank_amount=900) pe.reference_no = "1" pe.reference_date = "2016-01-01" - + self.assertEqual(pe.difference_amount, 100) - + pe.append("deductions", { "account": "_Test Exchange Gain/Loss - _TC", "cost_center": "_Test Cost Center - _TC", @@ -124,15 +124,15 @@ class TestPaymentEntry(unittest.TestCase): }) pe.insert() pe.submit() - + expected_gle = dict((d[0], d) for d in [ ["_Test Receivable USD - _TC", 0, 1000, si.name], ["_Test Bank - _TC", 900, 0, None], ["_Test Exchange Gain/Loss - _TC", 100.0, 0, None], ]) - + self.validate_gl_entries(pe.name, expected_gle) - + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 80) @@ -163,7 +163,7 @@ class TestPaymentEntry(unittest.TestCase): pe.source_exchange_rate, 65.1, "{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1) ) - + def test_internal_transfer_usd_to_inr(self): pe = frappe.new_doc("Payment Entry") pe.payment_type = "Internal Transfer" @@ -175,31 +175,31 @@ class TestPaymentEntry(unittest.TestCase): pe.received_amount = 4500 pe.reference_no = "2" pe.reference_date = nowdate() - + pe.setup_party_account_field() pe.set_missing_values() pe.set_exchange_rate() pe.set_amounts() - + self.assertEquals(pe.difference_amount, 500) - + pe.append("deductions", { "account": "_Test Exchange Gain/Loss - _TC", "cost_center": "_Test Cost Center - _TC", "amount": 500 }) - + pe.insert() pe.submit() - + expected_gle = dict((d[0], d) for d in [ ["_Test Bank USD - _TC", 0, 5000, None], ["_Test Bank - _TC", 4500, 0, None], ["_Test Exchange Gain/Loss - _TC", 500.0, 0, None], ]) - + self.validate_gl_entries(pe.name, expected_gle) - + def test_payment_against_negative_sales_invoice(self): pe1 = frappe.new_doc("Payment Entry") pe1.payment_type = "Pay" @@ -209,33 +209,33 @@ class TestPaymentEntry(unittest.TestCase): pe1.paid_from = "_Test Cash - _TC" pe1.paid_amount = 100 pe1.received_amount = 100 - + self.assertRaises(InvalidPaymentEntry, pe1.validate) - + si1 = create_sales_invoice() - + # create full payment entry against si1 pe2 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC") pe2.insert() pe2.submit() - + # create return entry against si1 create_sales_invoice(is_return=1, return_against=si1.name, qty=-1) si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount") self.assertEqual(si1_outstanding, -100) - + # pay more than outstanding against si1 pe3 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC") pe3.paid_amount = pe3.received_amount = 300 self.assertRaises(InvalidPaymentEntry, pe3.validate) - + # pay negative outstanding against si1 pe3.paid_to = "Debtors - _TC" pe3.paid_amount = pe3.received_amount = 100 - + pe3.insert() pe3.submit() - + expected_gle = dict((d[0], d) for d in [ ["Debtors - _TC", 100, 0, si1.name], ["_Test Cash - _TC", 0, 100, None] @@ -251,10 +251,10 @@ class TestPaymentEntry(unittest.TestCase): outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")) self.assertEqual(outstanding_amount, -100) - + def validate_gl_entries(self, voucher_no, expected_gle): gl_entries = self.get_gle(voucher_no) - + self.assertTrue(gl_entries) for i, gle in enumerate(gl_entries): @@ -262,7 +262,7 @@ class TestPaymentEntry(unittest.TestCase): self.assertEquals(expected_gle[gle.account][1], gle.debit) self.assertEquals(expected_gle[gle.account][2], gle.credit) self.assertEquals(expected_gle[gle.account][3], gle.against_voucher) - + def get_gle(self, voucher_no): return frappe.db.sql("""select account, debit, credit, against_voucher from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s diff --git a/erpnext/docs/user/manual/en/accounts/payment-entry.md b/erpnext/docs/user/manual/en/accounts/payment-entry.md index a6dbde04a8..ae82eb21f4 100644 --- a/erpnext/docs/user/manual/en/accounts/payment-entry.md +++ b/erpnext/docs/user/manual/en/accounts/payment-entry.md @@ -6,6 +6,7 @@ Payment Entry can be made against following transactions. 2. Purchase Invoice. 3. Sales Order (Advance Payment) 4. Purchase Order (Advance Payment) + 5. Expense Claim ####Step 1: Make Payment @@ -21,7 +22,7 @@ In the Payment Entry, select Mode of Payment (eg: Bank, Cash, Wire Transfer). In ####Step 3: Payment Amount -Enter actual payment amount received from the Customer or paid to the Supplier. +Enter actual payment amount received from the Customer or paid to the Supplier or Employee. Making Payment @@ -90,7 +91,7 @@ Following internal transfers can be managed from the Payment Entry. - Updating opening balance in an Accounts. - Fixed Asset Depreciation entry. - For adjusting Credit Note against Sales Invoice and Debit Note against Purchase Invoice, incase there is no payment happening at all. - + 4. Payment Entries are used if a cheque is printed. * * * ## Managing Outstanding Payments diff --git a/erpnext/docs/user/manual/en/human-resources/expense-claim.md b/erpnext/docs/user/manual/en/human-resources/expense-claim.md index 718e7fd6f6..2d7432f639 100644 --- a/erpnext/docs/user/manual/en/human-resources/expense-claim.md +++ b/erpnext/docs/user/manual/en/human-resources/expense-claim.md @@ -39,11 +39,18 @@ To make payment against the expense claim, user has to click on Make > Bank Entr Expense Claim #### Payment Entry -Expense Claim - +Expense Claim Note: This amount should not be clubbed with Salary because the amount will then be taxable to the Employee. +Alternatively, a Payment Entry can be made for an employee and all outstanding Expense Claims will be pulled in. + +> Accounts > Payment Entry > New Payment Entry + +Set the Payment Type to "Pay", the Party Type to Employee, the Party to the employee being paid and the account being paid +from. All outstanding expense claims will be pulled in and payments amounts can be allocated to each expense. +Expense Claim + ### Linking with Task & Project * To Link Expense Claim with Task or Project specify the Task or the Project while making an Expense Claim diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 016e8fa550..bcde2c010c 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -14,7 +14,7 @@ class TestExpenseClaim(unittest.TestCase): frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """) frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """) - + frappe.get_doc({ "project_name": "_Test Project 1", "doctype": "Project", @@ -25,7 +25,7 @@ class TestExpenseClaim(unittest.TestCase): task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"}) payable_account = get_payable_account("Wind Power LLC") - expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name) + make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) @@ -40,7 +40,7 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) - + def test_expense_claim_status(self): payable_account = get_payable_account("Wind Power LLC") expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP") @@ -54,7 +54,7 @@ class TestExpenseClaim(unittest.TestCase): expense_claim = frappe.get_doc("Expense Claim", expense_claim.name) self.assertEqual(expense_claim.status, "Paid") - + je.cancel() expense_claim = frappe.get_doc("Expense Claim", expense_claim.name) self.assertEqual(expense_claim.status, "Unpaid") @@ -63,7 +63,7 @@ class TestExpenseClaim(unittest.TestCase): payable_account = get_payable_account("Wind Power LLC") expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP") expense_claim.submit() - + gl_entries = frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Expense Claim' and voucher_no=%s order by account asc""", expense_claim.name, as_dict=1) @@ -100,7 +100,7 @@ class TestExpenseClaim(unittest.TestCase): def get_payable_account(company): return frappe.db.get_value('Company', company, 'default_payable_account') - + def make_expense_claim(payable_account,claim_amount, sanctioned_amount, company, account, project=None, task_name=None): expense_claim = frappe.get_doc({ "doctype": "Expense Claim", @@ -118,5 +118,5 @@ def make_expense_claim(payable_account,claim_amount, sanctioned_amount, company, expense_claim.submit() return expense_claim - +