Update manuals. Fix codacy issues

This commit is contained in:
Ben Cornwell-Mott 2017-07-17 11:18:28 -07:00 committed by Nabin Hait
parent 118ceda46f
commit 38d4be8325
5 changed files with 202 additions and 194 deletions

View File

@ -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,

View File

@ -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

View File

@ -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.
<img class="screenshot" alt="Making Payment" src="/docs/assets/img/accounts/payment-entry-3.png">
@ -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

View File

@ -39,11 +39,18 @@ To make payment against the expense claim, user has to click on Make > Bank Entr
<img class="screenshot" alt="Expense Claim" src="/docs/assets/img/human-resources/payment.png">
#### Payment Entry
<img class="screenshot" alt="Expense Claim" src="/docs/assets/img/human-resources/payment_entry.png">
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/payment_entry.png">
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.
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/expense_claim_payment_entry.png">
### Linking with Task & Project
* To Link Expense Claim with Task or Project specify the Task or the Project while making an Expense Claim

View File

@ -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