Merge branch 'develop' of https://github.com/frappe/erpnext into loan_patch_fix
This commit is contained in:
commit
68fce6d029
14
.editorconfig
Normal file
14
.editorconfig
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Root editor config file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Common settings
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# python, js indentation settings
|
||||||
|
[{*.py,*.js}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
@ -9,5 +9,6 @@
|
|||||||
"root_login": "root",
|
"root_login": "root",
|
||||||
"root_password": "travis",
|
"root_password": "travis",
|
||||||
"host_name": "http://test_site:8000",
|
"host_name": "http://test_site:8000",
|
||||||
"install_apps": ["erpnext"]
|
"install_apps": ["erpnext"],
|
||||||
|
"throttle_user_limit": 100
|
||||||
}
|
}
|
||||||
@ -245,6 +245,9 @@ def get():
|
|||||||
"account_number": "2200"
|
"account_number": "2200"
|
||||||
},
|
},
|
||||||
_("Duties and Taxes"): {
|
_("Duties and Taxes"): {
|
||||||
|
_("TDS Payable"): {
|
||||||
|
"account_number": "2310"
|
||||||
|
},
|
||||||
"account_type": "Tax",
|
"account_type": "Tax",
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"account_number": "2300"
|
"account_number": "2300"
|
||||||
|
|||||||
@ -9,11 +9,13 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
|
|||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||||
from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments
|
from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments
|
||||||
|
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||||
|
|
||||||
test_dependencies = ["Item", "Cost Center"]
|
test_dependencies = ["Item", "Cost Center"]
|
||||||
|
|
||||||
class TestBankTransaction(unittest.TestCase):
|
class TestBankTransaction(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
make_pos_profile()
|
||||||
add_transactions()
|
add_transactions()
|
||||||
add_payments()
|
add_payments()
|
||||||
|
|
||||||
@ -27,6 +29,9 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
frappe.db.sql("""delete from `tabPayment Entry Reference`""")
|
frappe.db.sql("""delete from `tabPayment Entry Reference`""")
|
||||||
frappe.db.sql("""delete from `tabPayment Entry`""")
|
frappe.db.sql("""delete from `tabPayment Entry`""")
|
||||||
|
|
||||||
|
# Delete POS Profile
|
||||||
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
|
||||||
frappe.flags.test_bank_transactions_created = False
|
frappe.flags.test_bank_transactions_created = False
|
||||||
frappe.flags.test_payments_created = False
|
frappe.flags.test_payments_created = False
|
||||||
|
|
||||||
|
|||||||
@ -137,6 +137,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
"cost_center": erpnext.get_default_cost_center(self.company)
|
"cost_center": erpnext.get_default_cost_center(self.company)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if self.bank_charges:
|
||||||
je.append("accounts", {
|
je.append("accounts", {
|
||||||
"account": self.bank_charges_account,
|
"account": self.bank_charges_account,
|
||||||
"debit_in_account_currency": flt(self.bank_charges),
|
"debit_in_account_currency": flt(self.bank_charges),
|
||||||
|
|||||||
@ -80,6 +80,7 @@ class TestInvoiceDiscounting(unittest.TestCase):
|
|||||||
short_term_loan=self.short_term_loan,
|
short_term_loan=self.short_term_loan,
|
||||||
bank_charges_account=self.bank_charges_account,
|
bank_charges_account=self.bank_charges_account,
|
||||||
bank_account=self.bank_account,
|
bank_account=self.bank_account,
|
||||||
|
bank_charges=100
|
||||||
)
|
)
|
||||||
|
|
||||||
je = inv_disc.create_disbursement_entry()
|
je = inv_disc.create_disbursement_entry()
|
||||||
@ -289,6 +290,7 @@ def create_invoice_discounting(invoices, **args):
|
|||||||
inv_disc.bank_account=args.bank_account
|
inv_disc.bank_account=args.bank_account
|
||||||
inv_disc.loan_start_date = args.start or nowdate()
|
inv_disc.loan_start_date = args.start or nowdate()
|
||||||
inv_disc.loan_period = args.period or 30
|
inv_disc.loan_period = args.period or 30
|
||||||
|
inv_disc.bank_charges = flt(args.bank_charges)
|
||||||
|
|
||||||
for d in invoices:
|
for d in invoices:
|
||||||
inv_disc.append("invoices", {
|
inv_disc.append("invoices", {
|
||||||
|
|||||||
@ -34,6 +34,7 @@ class JournalEntry(AccountsController):
|
|||||||
self.validate_entries_for_advance()
|
self.validate_entries_for_advance()
|
||||||
self.validate_multi_currency()
|
self.validate_multi_currency()
|
||||||
self.set_amounts_in_company_currency()
|
self.set_amounts_in_company_currency()
|
||||||
|
self.validate_debit_credit_amount()
|
||||||
self.validate_total_debit_and_credit()
|
self.validate_total_debit_and_credit()
|
||||||
self.validate_against_jv()
|
self.validate_against_jv()
|
||||||
self.validate_reference_doc()
|
self.validate_reference_doc()
|
||||||
@ -368,6 +369,11 @@ class JournalEntry(AccountsController):
|
|||||||
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
|
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
|
||||||
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
|
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
|
||||||
|
|
||||||
|
def validate_debit_credit_amount(self):
|
||||||
|
for d in self.get('accounts'):
|
||||||
|
if not flt(d.debit) and not flt(d.credit):
|
||||||
|
frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx))
|
||||||
|
|
||||||
def validate_total_debit_and_credit(self):
|
def validate_total_debit_and_credit(self):
|
||||||
self.set_total_debit_credit()
|
self.set_total_debit_credit()
|
||||||
if self.difference:
|
if self.difference:
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
cur_frm.set_query("default_account", "accounts", function(doc, cdt, cdn) {
|
frappe.ui.form.on('Mode of Payment', {
|
||||||
var d = locals[cdt][cdn];
|
setup: function(frm) {
|
||||||
return{
|
frm.set_query("default_account", "accounts", function(doc, cdt, cdn) {
|
||||||
|
let d = locals[cdt][cdn];
|
||||||
|
return {
|
||||||
filters: [
|
filters: [
|
||||||
['Account', 'account_type', 'in', 'Bank, Cash, Receivable'],
|
['Account', 'account_type', 'in', 'Bank, Cash, Receivable'],
|
||||||
['Account', 'is_group', '=', 0],
|
['Account', 'is_group', '=', 0],
|
||||||
['Account', 'company', '=', d.company]
|
['Account', 'company', '=', d.company]
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
@ -155,7 +155,8 @@ class OpeningInvoiceCreationTool(Document):
|
|||||||
"posting_date": row.posting_date,
|
"posting_date": row.posting_date,
|
||||||
frappe.scrub(row.party_type): row.party,
|
frappe.scrub(row.party_type): row.party,
|
||||||
"is_pos": 0,
|
"is_pos": 0,
|
||||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice"
|
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
|
||||||
|
"update_stock": 0
|
||||||
})
|
})
|
||||||
|
|
||||||
accounting_dimension = get_accounting_dimensions()
|
accounting_dimension = get_accounting_dimensions()
|
||||||
|
|||||||
@ -7,17 +7,24 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
test_dependencies = ["Customer", "Supplier"]
|
test_dependencies = ["Customer", "Supplier"]
|
||||||
|
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||||
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
||||||
|
|
||||||
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||||
def make_invoices(self, invoice_type="Sales"):
|
def setUp(self):
|
||||||
|
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||||
|
make_company()
|
||||||
|
|
||||||
|
def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None):
|
||||||
doc = frappe.get_single("Opening Invoice Creation Tool")
|
doc = frappe.get_single("Opening Invoice Creation Tool")
|
||||||
args = get_opening_invoice_creation_dict(invoice_type=invoice_type)
|
args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
|
||||||
|
party_1=party_1, party_2=party_2)
|
||||||
doc.update(args)
|
doc.update(args)
|
||||||
return doc.make_invoices()
|
return doc.make_invoices()
|
||||||
|
|
||||||
def test_opening_sales_invoice_creation(self):
|
def test_opening_sales_invoice_creation(self):
|
||||||
invoices = self.make_invoices()
|
property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check")
|
||||||
|
invoices = self.make_invoices(company="_Test Opening Invoice Company")
|
||||||
|
|
||||||
self.assertEqual(len(invoices), 2)
|
self.assertEqual(len(invoices), 2)
|
||||||
expected_value = {
|
expected_value = {
|
||||||
@ -27,6 +34,13 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
self.check_expected_values(invoices, expected_value)
|
self.check_expected_values(invoices, expected_value)
|
||||||
|
|
||||||
|
si = frappe.get_doc("Sales Invoice", invoices[0])
|
||||||
|
|
||||||
|
# Check if update stock is not enabled
|
||||||
|
self.assertEqual(si.update_stock, 0)
|
||||||
|
|
||||||
|
property_setter.delete()
|
||||||
|
|
||||||
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
||||||
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
||||||
|
|
||||||
@ -36,7 +50,7 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
self.assertEqual(si.get(field, ""), expected_value[invoice_idx][field_idx])
|
self.assertEqual(si.get(field, ""), expected_value[invoice_idx][field_idx])
|
||||||
|
|
||||||
def test_opening_purchase_invoice_creation(self):
|
def test_opening_purchase_invoice_creation(self):
|
||||||
invoices = self.make_invoices(invoice_type="Purchase")
|
invoices = self.make_invoices(invoice_type="Purchase", company="_Test Opening Invoice Company")
|
||||||
|
|
||||||
self.assertEqual(len(invoices), 2)
|
self.assertEqual(len(invoices), 2)
|
||||||
expected_value = {
|
expected_value = {
|
||||||
@ -46,6 +60,32 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
self.check_expected_values(invoices, expected_value, "Purchase")
|
self.check_expected_values(invoices, expected_value, "Purchase")
|
||||||
|
|
||||||
|
def test_opening_sales_invoice_creation_with_missing_debit_account(self):
|
||||||
|
company = "_Test Opening Invoice Company"
|
||||||
|
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
|
||||||
|
|
||||||
|
old_default_receivable_account = frappe.db.get_value("Company", company, "default_receivable_account")
|
||||||
|
frappe.db.set_value("Company", company, "default_receivable_account", "")
|
||||||
|
|
||||||
|
if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"):
|
||||||
|
cc = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "_Test Opening Invoice Company",
|
||||||
|
"is_group": 1, "company": "_Test Opening Invoice Company"})
|
||||||
|
cc.insert(ignore_mandatory=True)
|
||||||
|
cc2 = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "Main", "is_group": 0,
|
||||||
|
"company": "_Test Opening Invoice Company", "parent_cost_center": cc.name})
|
||||||
|
cc2.insert()
|
||||||
|
|
||||||
|
frappe.db.set_value("Company", company, "cost_center", "Main - _TOIC")
|
||||||
|
|
||||||
|
self.make_invoices(company="_Test Opening Invoice Company", party_1=party_1, party_2=party_2)
|
||||||
|
|
||||||
|
# Check if missing debit account error raised
|
||||||
|
error_log = frappe.db.exists("Error Log", {"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]})
|
||||||
|
self.assertTrue(error_log)
|
||||||
|
|
||||||
|
# teardown
|
||||||
|
frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
|
||||||
|
|
||||||
def get_opening_invoice_creation_dict(**args):
|
def get_opening_invoice_creation_dict(**args):
|
||||||
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
|
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
|
||||||
company = args.get("company", "_Test Company")
|
company = args.get("company", "_Test Company")
|
||||||
@ -57,7 +97,7 @@ def get_opening_invoice_creation_dict(**args):
|
|||||||
{
|
{
|
||||||
"qty": 1.0,
|
"qty": 1.0,
|
||||||
"outstanding_amount": 300,
|
"outstanding_amount": 300,
|
||||||
"party": "_Test {0}".format(party),
|
"party": args.get("party_1") or "_Test {0}".format(party),
|
||||||
"item_name": "Opening Item",
|
"item_name": "Opening Item",
|
||||||
"due_date": "2016-09-10",
|
"due_date": "2016-09-10",
|
||||||
"posting_date": "2016-09-05",
|
"posting_date": "2016-09-05",
|
||||||
@ -66,7 +106,7 @@ def get_opening_invoice_creation_dict(**args):
|
|||||||
{
|
{
|
||||||
"qty": 2.0,
|
"qty": 2.0,
|
||||||
"outstanding_amount": 250,
|
"outstanding_amount": 250,
|
||||||
"party": "_Test {0} 1".format(party),
|
"party": args.get("party_2") or "_Test {0} 1".format(party),
|
||||||
"item_name": "Opening Item",
|
"item_name": "Opening Item",
|
||||||
"due_date": "2016-09-10",
|
"due_date": "2016-09-10",
|
||||||
"posting_date": "2016-09-05",
|
"posting_date": "2016-09-05",
|
||||||
@ -77,3 +117,30 @@ def get_opening_invoice_creation_dict(**args):
|
|||||||
|
|
||||||
invoice_dict.update(args)
|
invoice_dict.update(args)
|
||||||
return invoice_dict
|
return invoice_dict
|
||||||
|
|
||||||
|
def make_company():
|
||||||
|
if frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||||
|
return frappe.get_doc("Company", "_Test Opening Invoice Company")
|
||||||
|
|
||||||
|
company = frappe.new_doc("Company")
|
||||||
|
company.company_name = "_Test Opening Invoice Company"
|
||||||
|
company.abbr = "_TOIC"
|
||||||
|
company.default_currency = "INR"
|
||||||
|
company.country = "India"
|
||||||
|
company.insert()
|
||||||
|
return company
|
||||||
|
|
||||||
|
def make_customer(customer=None):
|
||||||
|
customer_name = customer or "Opening Customer"
|
||||||
|
customer = frappe.get_doc({
|
||||||
|
"doctype": "Customer",
|
||||||
|
"customer_name": customer_name,
|
||||||
|
"customer_group": "All Customer Groups",
|
||||||
|
"customer_type": "Company",
|
||||||
|
"territory": "All Territories"
|
||||||
|
})
|
||||||
|
if not frappe.db.exists("Customer", customer_name):
|
||||||
|
customer.insert(ignore_permissions=True)
|
||||||
|
return customer.name
|
||||||
|
else:
|
||||||
|
return frappe.db.exists("Customer", customer_name)
|
||||||
@ -202,15 +202,30 @@ class PaymentEntry(AccountsController):
|
|||||||
# if account_type not in account_types:
|
# if account_type not in account_types:
|
||||||
# frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types)))
|
# frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types)))
|
||||||
|
|
||||||
def set_exchange_rate(self):
|
def set_exchange_rate(self, ref_doc=None):
|
||||||
|
self.set_source_exchange_rate(ref_doc)
|
||||||
|
self.set_target_exchange_rate(ref_doc)
|
||||||
|
|
||||||
|
def set_source_exchange_rate(self, ref_doc=None):
|
||||||
if self.paid_from and not self.source_exchange_rate:
|
if self.paid_from and not self.source_exchange_rate:
|
||||||
if self.paid_from_account_currency == self.company_currency:
|
if self.paid_from_account_currency == self.company_currency:
|
||||||
self.source_exchange_rate = 1
|
self.source_exchange_rate = 1
|
||||||
else:
|
else:
|
||||||
|
if ref_doc:
|
||||||
|
if self.paid_from_account_currency == ref_doc.currency:
|
||||||
|
self.source_exchange_rate = ref_doc.get("exchange_rate")
|
||||||
|
|
||||||
|
if not self.source_exchange_rate:
|
||||||
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)
|
self.company_currency, self.posting_date)
|
||||||
|
|
||||||
|
def set_target_exchange_rate(self, ref_doc=None):
|
||||||
if self.paid_to and not self.target_exchange_rate:
|
if self.paid_to and not self.target_exchange_rate:
|
||||||
|
if ref_doc:
|
||||||
|
if self.paid_to_account_currency == ref_doc.currency:
|
||||||
|
self.target_exchange_rate = ref_doc.get("exchange_rate")
|
||||||
|
|
||||||
|
if 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)
|
self.company_currency, self.posting_date)
|
||||||
|
|
||||||
@ -282,9 +297,10 @@ class PaymentEntry(AccountsController):
|
|||||||
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
|
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
|
||||||
|
|
||||||
for k, v in no_oustanding_refs.items():
|
for k, v in no_oustanding_refs.items():
|
||||||
frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.<br><br>\
|
frappe.msgprint(
|
||||||
If this is undesirable please cancel the corresponding Payment Entry.")
|
_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.")
|
||||||
.format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")),
|
.format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount"))
|
||||||
|
+ "<br><br>" + _("If this is undesirable please cancel the corresponding Payment Entry."),
|
||||||
title=_("Warning"), indicator="orange")
|
title=_("Warning"), indicator="orange")
|
||||||
|
|
||||||
|
|
||||||
@ -909,22 +925,24 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
||||||
elif reference_doctype != "Journal Entry":
|
elif reference_doctype != "Journal Entry":
|
||||||
if party_account_currency == company_currency:
|
|
||||||
if ref_doc.doctype == "Expense Claim":
|
if ref_doc.doctype == "Expense Claim":
|
||||||
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
||||||
elif ref_doc.doctype == "Employee Advance":
|
elif ref_doc.doctype == "Employee Advance":
|
||||||
total_amount = ref_doc.advance_amount
|
total_amount = ref_doc.advance_amount
|
||||||
else:
|
exchange_rate = ref_doc.get("exchange_rate")
|
||||||
|
if party_account_currency != ref_doc.currency:
|
||||||
|
total_amount = flt(total_amount) * flt(exchange_rate)
|
||||||
|
if not total_amount:
|
||||||
|
if party_account_currency == company_currency:
|
||||||
total_amount = ref_doc.base_grand_total
|
total_amount = ref_doc.base_grand_total
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
else:
|
else:
|
||||||
total_amount = ref_doc.grand_total
|
total_amount = ref_doc.grand_total
|
||||||
|
if not exchange_rate:
|
||||||
# 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
|
# or get it based on the posting date of the ref doc.
|
||||||
exchange_rate = ref_doc.get("conversion_rate") or \
|
exchange_rate = ref_doc.get("conversion_rate") or \
|
||||||
get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
|
get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
|
||||||
|
|
||||||
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||||
bill_no = ref_doc.get("bill_no")
|
bill_no = ref_doc.get("bill_no")
|
||||||
@ -932,11 +950,15 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\
|
outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\
|
||||||
- flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount"))
|
- flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount"))
|
||||||
elif reference_doctype == "Employee Advance":
|
elif reference_doctype == "Employee Advance":
|
||||||
outstanding_amount = ref_doc.advance_amount - flt(ref_doc.paid_amount)
|
outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount))
|
||||||
|
if party_account_currency != ref_doc.currency:
|
||||||
|
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
|
||||||
|
if party_account_currency == company_currency:
|
||||||
|
exchange_rate = 1
|
||||||
else:
|
else:
|
||||||
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
||||||
else:
|
else:
|
||||||
# Get the exchange rate based on the posting date of the ref doc
|
# 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,
|
||||||
company_currency, ref_doc.posting_date)
|
company_currency, ref_doc.posting_date)
|
||||||
|
|
||||||
@ -948,102 +970,104 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
"bill_no": bill_no
|
"bill_no": bill_no
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def get_amounts_based_on_reference_doctype(reference_doctype, ref_doc, party_account_currency, company_currency, reference_name):
|
||||||
|
total_amount, outstanding_amount, exchange_rate = None
|
||||||
|
if reference_doctype == "Fees":
|
||||||
|
total_amount = ref_doc.get("grand_total")
|
||||||
|
exchange_rate = 1
|
||||||
|
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||||
|
elif reference_doctype == "Dunning":
|
||||||
|
total_amount = ref_doc.get("dunning_amount")
|
||||||
|
exchange_rate = 1
|
||||||
|
outstanding_amount = ref_doc.get("dunning_amount")
|
||||||
|
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
|
||||||
|
total_amount = ref_doc.get("total_amount")
|
||||||
|
if ref_doc.multi_currency:
|
||||||
|
exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
|
||||||
|
else:
|
||||||
|
exchange_rate = 1
|
||||||
|
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
||||||
|
|
||||||
|
return total_amount, outstanding_amount, exchange_rate
|
||||||
|
|
||||||
|
def get_amounts_based_on_ref_doc(reference_doctype, ref_doc, party_account_currency, company_currency):
|
||||||
|
total_amount, outstanding_amount, exchange_rate = None
|
||||||
|
if ref_doc.doctype == "Expense Claim":
|
||||||
|
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
||||||
|
elif ref_doc.doctype == "Employee Advance":
|
||||||
|
total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc)
|
||||||
|
|
||||||
|
if not total_amount:
|
||||||
|
total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency(
|
||||||
|
party_account_currency, company_currency, ref_doc)
|
||||||
|
|
||||||
|
if not exchange_rate:
|
||||||
|
# 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, company_currency, ref_doc.posting_date)
|
||||||
|
|
||||||
|
outstanding_amount, exchange_rate, bill_no = get_bill_no_and_update_amounts(
|
||||||
|
reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency)
|
||||||
|
|
||||||
|
return total_amount, outstanding_amount, exchange_rate, bill_no
|
||||||
|
|
||||||
|
def get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc):
|
||||||
|
total_amount = ref_doc.advance_amount
|
||||||
|
exchange_rate = ref_doc.get("exchange_rate")
|
||||||
|
if party_account_currency != ref_doc.currency:
|
||||||
|
total_amount = flt(total_amount) * flt(exchange_rate)
|
||||||
|
|
||||||
|
return total_amount, exchange_rate
|
||||||
|
|
||||||
|
def get_total_amount_exchange_rate_base_on_currency(party_account_currency, company_currency, ref_doc):
|
||||||
|
exchange_rate = None
|
||||||
|
if party_account_currency == company_currency:
|
||||||
|
total_amount = ref_doc.base_grand_total
|
||||||
|
exchange_rate = 1
|
||||||
|
else:
|
||||||
|
total_amount = ref_doc.grand_total
|
||||||
|
|
||||||
|
return total_amount, exchange_rate
|
||||||
|
|
||||||
|
def get_bill_no_and_update_amounts(reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency):
|
||||||
|
outstanding_amount, bill_no = None
|
||||||
|
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
|
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||||
|
bill_no = ref_doc.get("bill_no")
|
||||||
|
elif reference_doctype == "Expense Claim":
|
||||||
|
outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\
|
||||||
|
- flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount"))
|
||||||
|
elif reference_doctype == "Employee Advance":
|
||||||
|
outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount))
|
||||||
|
if party_account_currency != ref_doc.currency:
|
||||||
|
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
|
||||||
|
if party_account_currency == company_currency:
|
||||||
|
exchange_rate = 1
|
||||||
|
else:
|
||||||
|
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
||||||
|
|
||||||
|
return outstanding_amount, exchange_rate, bill_no
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
|
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
|
||||||
|
reference_doc = None
|
||||||
doc = frappe.get_doc(dt, dn)
|
doc = frappe.get_doc(dt, dn)
|
||||||
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
|
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))
|
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
|
||||||
|
|
||||||
if dt in ("Sales Invoice", "Sales Order", "Dunning"):
|
party_type = set_party_type(dt)
|
||||||
party_type = "Customer"
|
party_account = set_party_account(dt, dn, doc, party_type)
|
||||||
elif dt in ("Purchase Invoice", "Purchase Order"):
|
party_account_currency = set_party_account_currency(dt, party_account, doc)
|
||||||
party_type = "Supplier"
|
payment_type = set_payment_type(dt, doc)
|
||||||
elif dt in ("Expense Claim", "Employee Advance"):
|
grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(party_amount, dt, party_account_currency, doc)
|
||||||
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", "Dunning") 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
|
|
||||||
elif dt == "Dunning":
|
|
||||||
grand_total = doc.grand_total
|
|
||||||
outstanding_amount = doc.grand_total
|
|
||||||
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 or cash
|
||||||
bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"),
|
bank = get_bank_cash_account(doc, bank_account)
|
||||||
account=bank_account)
|
|
||||||
|
|
||||||
if not bank:
|
paid_amount, received_amount = set_paid_amount_and_received_amount(
|
||||||
bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"),
|
dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc)
|
||||||
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.get('conversion_rate', 1)
|
|
||||||
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.get('conversion_rate', 1)
|
|
||||||
|
|
||||||
pe = frappe.new_doc("Payment Entry")
|
pe = frappe.new_doc("Payment Entry")
|
||||||
pe.payment_type = payment_type
|
pe.payment_type = payment_type
|
||||||
@ -1115,10 +1139,120 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
pe.setup_party_account_field()
|
pe.setup_party_account_field()
|
||||||
pe.set_missing_values()
|
pe.set_missing_values()
|
||||||
if party_account and bank:
|
if party_account and bank:
|
||||||
pe.set_exchange_rate()
|
if dt == "Employee Advance":
|
||||||
|
reference_doc = doc
|
||||||
|
pe.set_exchange_rate(ref_doc=reference_doc)
|
||||||
pe.set_amounts()
|
pe.set_amounts()
|
||||||
return pe
|
return pe
|
||||||
|
|
||||||
|
def get_bank_cash_account(doc, bank_account):
|
||||||
|
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)
|
||||||
|
|
||||||
|
return bank
|
||||||
|
|
||||||
|
def set_party_type(dt):
|
||||||
|
if dt in ("Sales Invoice", "Sales Order", "Dunning"):
|
||||||
|
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"
|
||||||
|
return party_type
|
||||||
|
|
||||||
|
def set_party_account(dt, dn, doc, party_type):
|
||||||
|
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)
|
||||||
|
return party_account
|
||||||
|
|
||||||
|
def set_party_account_currency(dt, party_account, doc):
|
||||||
|
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)
|
||||||
|
return party_account_currency
|
||||||
|
|
||||||
|
def set_payment_type(dt, doc):
|
||||||
|
if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \
|
||||||
|
or (dt=="Purchase Invoice" and doc.outstanding_amount < 0):
|
||||||
|
payment_type = "Receive"
|
||||||
|
else:
|
||||||
|
payment_type = "Pay"
|
||||||
|
return payment_type
|
||||||
|
|
||||||
|
def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_currency, doc):
|
||||||
|
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 = flt(doc.advance_amount)
|
||||||
|
outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
|
||||||
|
if party_account_currency != doc.currency:
|
||||||
|
grand_total = flt(doc.advance_amount) * flt(doc.exchange_rate)
|
||||||
|
outstanding_amount = (flt(doc.advance_amount) - flt(doc.paid_amount)) * flt(doc.exchange_rate)
|
||||||
|
elif dt == "Fees":
|
||||||
|
grand_total = doc.grand_total
|
||||||
|
outstanding_amount = doc.outstanding_amount
|
||||||
|
elif dt == "Dunning":
|
||||||
|
grand_total = doc.grand_total
|
||||||
|
outstanding_amount = doc.grand_total
|
||||||
|
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)
|
||||||
|
return grand_total, outstanding_amount
|
||||||
|
|
||||||
|
def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc):
|
||||||
|
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.get('conversion_rate', 1)
|
||||||
|
if dt == "Employee Advance":
|
||||||
|
received_amount = paid_amount * doc.get('exchange_rate', 1)
|
||||||
|
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.get('conversion_rate', 1)
|
||||||
|
if dt == "Employee Advance":
|
||||||
|
paid_amount = received_amount * doc.get('exchange_rate', 1)
|
||||||
|
return paid_amount, received_amount
|
||||||
|
|
||||||
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||||
references = []
|
references = []
|
||||||
for payment_term in payment_schedule:
|
for payment_term in payment_schedule:
|
||||||
|
|||||||
@ -14,7 +14,6 @@
|
|||||||
"column_break_9",
|
"column_break_9",
|
||||||
"update_stock",
|
"update_stock",
|
||||||
"ignore_pricing_rule",
|
"ignore_pricing_rule",
|
||||||
"hide_unavailable_items",
|
|
||||||
"warehouse",
|
"warehouse",
|
||||||
"campaign",
|
"campaign",
|
||||||
"company_address",
|
"company_address",
|
||||||
@ -23,6 +22,9 @@
|
|||||||
"section_break_11",
|
"section_break_11",
|
||||||
"payments",
|
"payments",
|
||||||
"section_break_14",
|
"section_break_14",
|
||||||
|
"hide_images",
|
||||||
|
"hide_unavailable_items",
|
||||||
|
"auto_add_item_to_cart",
|
||||||
"item_groups",
|
"item_groups",
|
||||||
"column_break_16",
|
"column_break_16",
|
||||||
"customer_groups",
|
"customer_groups",
|
||||||
@ -124,7 +126,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "section_break_14",
|
"fieldname": "section_break_14",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Configuration"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Only show Items from these Item Groups",
|
"description": "Only show Items from these Item Groups",
|
||||||
@ -314,13 +317,25 @@
|
|||||||
"fieldname": "hide_unavailable_items",
|
"fieldname": "hide_unavailable_items",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Hide Unavailable Items"
|
"label": "Hide Unavailable Items"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "hide_images",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Hide Images"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "auto_add_item_to_cart",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Automatically Add Filtered Item To Cart"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-29 13:18:38.795925",
|
"modified": "2020-12-10 13:59:28.877572",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Profile",
|
"name": "POS Profile",
|
||||||
|
|||||||
@ -70,6 +70,7 @@ def get_items_list(pos_profile, company):
|
|||||||
""".format(cond=cond), tuple([company] + args_list), as_dict=1)
|
""".format(cond=cond), tuple([company] + args_list), as_dict=1)
|
||||||
|
|
||||||
def make_pos_profile(**args):
|
def make_pos_profile(**args):
|
||||||
|
frappe.db.sql("delete from `tabPOS Payment Method`")
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|||||||
@ -406,6 +406,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
||||||
"fieldname": "rate",
|
"fieldname": "rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
@ -469,6 +470,7 @@
|
|||||||
"options": "UOM"
|
"options": "UOM"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"description": "If rate is zero them item will be treated as \"Free Item\"",
|
||||||
"fieldname": "free_item_rate",
|
"fieldname": "free_item_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Rate"
|
"label": "Rate"
|
||||||
@ -563,7 +565,7 @@
|
|||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-28 16:53:14.416172",
|
"modified": "2020-12-04 00:36:24.698219",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
|||||||
@ -521,6 +521,22 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
||||||
item.delete()
|
item.delete()
|
||||||
|
|
||||||
|
def test_pricing_rule_for_transaction(self):
|
||||||
|
make_item("Water Flask 1")
|
||||||
|
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||||
|
make_pricing_rule(selling=1, min_qty=5, price_or_product_discount="Product",
|
||||||
|
apply_on="Transaction", free_item="Water Flask 1", free_qty=1, free_item_rate=10)
|
||||||
|
|
||||||
|
si = create_sales_invoice(qty=5, do_not_submit=True)
|
||||||
|
self.assertEquals(len(si.items), 2)
|
||||||
|
self.assertEquals(si.items[1].rate, 10)
|
||||||
|
|
||||||
|
si1 = create_sales_invoice(qty=2, do_not_submit=True)
|
||||||
|
self.assertEquals(len(si1.items), 1)
|
||||||
|
|
||||||
|
for doc in [si, si1]:
|
||||||
|
doc.delete()
|
||||||
|
|
||||||
def make_pricing_rule(**args):
|
def make_pricing_rule(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
@ -539,17 +555,20 @@ def make_pricing_rule(**args):
|
|||||||
"rate_or_discount": args.rate_or_discount or "Discount Percentage",
|
"rate_or_discount": args.rate_or_discount or "Discount Percentage",
|
||||||
"discount_percentage": args.discount_percentage or 0.0,
|
"discount_percentage": args.discount_percentage or 0.0,
|
||||||
"rate": args.rate or 0.0,
|
"rate": args.rate or 0.0,
|
||||||
"margin_type": args.margin_type,
|
|
||||||
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
||||||
"condition": args.condition or '',
|
"condition": args.condition or '',
|
||||||
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
|
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
|
||||||
})
|
})
|
||||||
|
|
||||||
if args.get("priority"):
|
for field in ["free_item", "free_qty", "free_item_rate", "priority",
|
||||||
doc.priority = args.get("priority")
|
"margin_type", "price_or_product_discount"]:
|
||||||
|
if args.get(field):
|
||||||
|
doc.set(field, args.get(field))
|
||||||
|
|
||||||
apply_on = doc.apply_on.replace(' ', '_').lower()
|
apply_on = doc.apply_on.replace(' ', '_').lower()
|
||||||
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
|
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
|
||||||
|
|
||||||
|
if doc.apply_on != "Transaction":
|
||||||
doc.append(child_table.get(doc.apply_on), {
|
doc.append(child_table.get(doc.apply_on), {
|
||||||
apply_on: args.get(apply_on) or "_Test Item"
|
apply_on: args.get(apply_on) or "_Test Item"
|
||||||
})
|
})
|
||||||
|
|||||||
@ -457,6 +457,9 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
||||||
doc.total, pricing_rules)
|
doc.total, pricing_rules)
|
||||||
|
|
||||||
|
if not pricing_rules:
|
||||||
|
remove_free_item(doc)
|
||||||
|
|
||||||
for d in pricing_rules:
|
for d in pricing_rules:
|
||||||
if d.price_or_product_discount == 'Price':
|
if d.price_or_product_discount == 'Price':
|
||||||
if d.apply_discount_on:
|
if d.apply_discount_on:
|
||||||
@ -480,6 +483,12 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
get_product_discount_rule(d, item_details, doc=doc)
|
get_product_discount_rule(d, item_details, doc=doc)
|
||||||
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||||
doc.set_missing_values()
|
doc.set_missing_values()
|
||||||
|
doc.calculate_taxes_and_totals()
|
||||||
|
|
||||||
|
def remove_free_item(doc):
|
||||||
|
for d in doc.items:
|
||||||
|
if d.is_free_item:
|
||||||
|
doc.remove(d)
|
||||||
|
|
||||||
def get_applied_pricing_rules(pricing_rules):
|
def get_applied_pricing_rules(pricing_rules):
|
||||||
if pricing_rules:
|
if pricing_rules:
|
||||||
@ -492,7 +501,7 @@ def get_applied_pricing_rules(pricing_rules):
|
|||||||
|
|
||||||
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||||
free_item = pricing_rule.free_item
|
free_item = pricing_rule.free_item
|
||||||
if pricing_rule.same_item:
|
if pricing_rule.same_item and pricing_rule.get("apply_on") != 'Transaction':
|
||||||
free_item = item_details.item_code or args.item_code
|
free_item = item_details.item_code or args.item_code
|
||||||
|
|
||||||
if not free_item:
|
if not free_item:
|
||||||
|
|||||||
@ -21,7 +21,7 @@ class TestProcessDeferredAccounting(unittest.TestCase):
|
|||||||
item.no_of_months = 12
|
item.no_of_months = 12
|
||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True)
|
si = create_sales_invoice(item=item.name, update_stock=0, posting_date="2019-01-10", do_not_submit=True)
|
||||||
si.items[0].enable_deferred_revenue = 1
|
si.items[0].enable_deferred_revenue = 1
|
||||||
si.items[0].service_start_date = "2019-01-10"
|
si.items[0].service_start_date = "2019-01-10"
|
||||||
si.items[0].service_end_date = "2019-03-15"
|
si.items[0].service_end_date = "2019-03-15"
|
||||||
|
|||||||
@ -15,6 +15,16 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
return (doc.qty<=doc.received_qty) ? "green" : "orange";
|
return (doc.qty<=doc.received_qty) ? "green" : "orange";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.frm.set_query("unrealized_profit_loss_account", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: doc.company,
|
||||||
|
is_group: 0,
|
||||||
|
root_type: "Liability",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onload: function() {
|
onload: function() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_auto_repeat": 1,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-21 16:16:39",
|
"creation": "2013-05-21 16:16:39",
|
||||||
@ -127,6 +126,7 @@
|
|||||||
"write_off_cost_center",
|
"write_off_cost_center",
|
||||||
"advances_section",
|
"advances_section",
|
||||||
"allocate_advances_automatically",
|
"allocate_advances_automatically",
|
||||||
|
"adjust_advance_taxes",
|
||||||
"get_advances",
|
"get_advances",
|
||||||
"advances",
|
"advances",
|
||||||
"payment_schedule_section",
|
"payment_schedule_section",
|
||||||
@ -152,9 +152,11 @@
|
|||||||
"is_opening",
|
"is_opening",
|
||||||
"against_expense_account",
|
"against_expense_account",
|
||||||
"column_break_63",
|
"column_break_63",
|
||||||
|
"unrealized_profit_loss_account",
|
||||||
"status",
|
"status",
|
||||||
"inter_company_invoice_reference",
|
"inter_company_invoice_reference",
|
||||||
"is_internal_supplier",
|
"is_internal_supplier",
|
||||||
|
"represents_company",
|
||||||
"remarks",
|
"remarks",
|
||||||
"subscription_section",
|
"subscription_section",
|
||||||
"from_date",
|
"from_date",
|
||||||
@ -1223,7 +1225,7 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled",
|
"options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled\nInternal Transfer",
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1330,13 +1332,37 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Project",
|
"label": "Project",
|
||||||
"options": "Project"
|
"options": "Project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "Taxes paid while advance payment will be adjusted against this invoice",
|
||||||
|
"fieldname": "adjust_advance_taxes",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Adjust Advance Taxes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.is_internal_supplier",
|
||||||
|
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
||||||
|
"fieldname": "unrealized_profit_loss_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Unrealized Profit / Loss Account",
|
||||||
|
"options": "Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.is_internal_supplier",
|
||||||
|
"description": "Company which internal supplier represents",
|
||||||
|
"fetch_from": "supplier.represents_company",
|
||||||
|
"fieldname": "represents_company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Represents Company",
|
||||||
|
"options": "Company"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-30 13:57:18.266978",
|
"modified": "2020-12-11 12:46:12.796378",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@ -147,6 +147,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
throw(_("Conversion rate cannot be 0 or 1"))
|
throw(_("Conversion rate cannot be 0 or 1"))
|
||||||
|
|
||||||
def validate_credit_to_acc(self):
|
def validate_credit_to_acc(self):
|
||||||
|
if not self.credit_to:
|
||||||
|
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
||||||
|
if not self.credit_to:
|
||||||
|
self.raise_missing_debit_credit_account_error("Supplier", self.supplier)
|
||||||
|
|
||||||
account = frappe.db.get_value("Account", self.credit_to,
|
account = frappe.db.get_value("Account", self.credit_to,
|
||||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||||
|
|
||||||
@ -201,8 +206,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
["Purchase Receipt", "purchase_receipt", "pr_detail"]
|
["Purchase Receipt", "purchase_receipt", "pr_detail"]
|
||||||
])
|
])
|
||||||
|
|
||||||
def validate_warehouse(self):
|
def validate_warehouse(self, for_validate=True):
|
||||||
if self.update_stock:
|
if self.update_stock and for_validate:
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if not d.warehouse:
|
if not d.warehouse:
|
||||||
frappe.throw(_("Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}").
|
frappe.throw(_("Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}").
|
||||||
@ -228,7 +233,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
if self.update_stock:
|
if self.update_stock:
|
||||||
self.validate_item_code()
|
self.validate_item_code()
|
||||||
self.validate_warehouse()
|
self.validate_warehouse(for_validate)
|
||||||
if auto_accounting_for_stock:
|
if auto_accounting_for_stock:
|
||||||
warehouse_account = get_warehouse_account_map(self.company)
|
warehouse_account = get_warehouse_account_map(self.company)
|
||||||
|
|
||||||
@ -444,6 +449,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.get_asset_gl_entry(gl_entries)
|
self.get_asset_gl_entry(gl_entries)
|
||||||
|
|
||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
|
self.make_internal_transfer_gl_entries(gl_entries)
|
||||||
|
|
||||||
gl_entries = make_regional_gl_entries(gl_entries, self)
|
gl_entries = make_regional_gl_entries(gl_entries, self)
|
||||||
|
|
||||||
@ -452,7 +458,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.make_payment_gl_entries(gl_entries)
|
self.make_payment_gl_entries(gl_entries)
|
||||||
self.make_write_off_gl_entry(gl_entries)
|
self.make_write_off_gl_entry(gl_entries)
|
||||||
self.make_gle_for_rounding_adjustment(gl_entries)
|
self.make_gle_for_rounding_adjustment(gl_entries)
|
||||||
|
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
def check_asset_cwip_enabled(self):
|
def check_asset_cwip_enabled(self):
|
||||||
@ -469,7 +474,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
||||||
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
||||||
|
|
||||||
if grand_total:
|
if grand_total and not self.is_internal_transfer():
|
||||||
# Didnot use base_grand_total to book rounding loss gle
|
# Didnot use base_grand_total to book rounding loss gle
|
||||||
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
||||||
self.precision("grand_total"))
|
self.precision("grand_total"))
|
||||||
@ -493,7 +498,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
def make_item_gl_entries(self, gl_entries):
|
def make_item_gl_entries(self, gl_entries):
|
||||||
# item gl entries
|
# item gl entries
|
||||||
stock_items = self.get_stock_items()
|
stock_items = self.get_stock_items()
|
||||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
|
||||||
if self.update_stock and self.auto_accounting_for_stock:
|
if self.update_stock and self.auto_accounting_for_stock:
|
||||||
warehouse_account = get_warehouse_account_map(self.company)
|
warehouse_account = get_warehouse_account_map(self.company)
|
||||||
|
|
||||||
@ -521,7 +525,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item, voucher_wise_stock_value, account_currency)
|
item, voucher_wise_stock_value, account_currency)
|
||||||
|
|
||||||
if item.from_warehouse:
|
if item.from_warehouse:
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": warehouse_account[item.warehouse]['account'],
|
"account": warehouse_account[item.warehouse]['account'],
|
||||||
"against": warehouse_account[item.from_warehouse]["account"],
|
"against": warehouse_account[item.from_warehouse]["account"],
|
||||||
@ -541,6 +544,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
|
"debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||||
}, warehouse_account[item.from_warehouse]["account_currency"], item=item))
|
}, warehouse_account[item.from_warehouse]["account_currency"], item=item))
|
||||||
|
|
||||||
|
# Do not book expense for transfer within same company transfer
|
||||||
|
if not self.is_internal_transfer():
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": item.expense_account,
|
"account": item.expense_account,
|
||||||
@ -827,7 +832,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
}, account_currency, item=tax)
|
}, account_currency, item=tax)
|
||||||
)
|
)
|
||||||
# accumulate valuation tax
|
# accumulate valuation tax
|
||||||
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount) \
|
||||||
|
and not self.is_internal_transfer():
|
||||||
if self.auto_accounting_for_stock and not tax.cost_center:
|
if self.auto_accounting_for_stock and not tax.cost_center:
|
||||||
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
|
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
|
||||||
valuation_tax.setdefault(tax.name, 0)
|
valuation_tax.setdefault(tax.name, 0)
|
||||||
@ -871,8 +877,19 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"credit": valuation_tax[tax.name],
|
"credit": valuation_tax[tax.name],
|
||||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||||
}, item=tax)
|
}, item=tax))
|
||||||
)
|
|
||||||
|
def make_internal_transfer_gl_entries(self, gl_entries):
|
||||||
|
if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
|
||||||
|
account_currency = get_account_currency(self.unrealized_profit_loss_account)
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": self.unrealized_profit_loss_account,
|
||||||
|
"against": self.supplier,
|
||||||
|
"credit": flt(self.total_taxes_and_charges),
|
||||||
|
"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
|
||||||
|
"cost_center": self.cost_center
|
||||||
|
}, account_currency, item=self))
|
||||||
|
|
||||||
def make_payment_gl_entries(self, gl_entries):
|
def make_payment_gl_entries(self, gl_entries):
|
||||||
# Make Cash GL Entries
|
# Make Cash GL Entries
|
||||||
@ -1032,7 +1049,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
updated_pr += update_billed_amount_based_on_po(d.po_detail, update_modified)
|
updated_pr += update_billed_amount_based_on_po(d.po_detail, update_modified)
|
||||||
|
|
||||||
for pr in set(updated_pr):
|
for pr in set(updated_pr):
|
||||||
frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(update_modified=update_modified)
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
|
||||||
|
pr_doc = frappe.get_doc("Purchase Receipt", pr)
|
||||||
|
update_billing_percentage(pr_doc, update_modified=update_modified)
|
||||||
|
|
||||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||||
self.due_date = None
|
self.due_date = None
|
||||||
@ -1088,7 +1107,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
status = "Cancelled"
|
status = "Cancelled"
|
||||||
elif self.docstatus == 1:
|
elif self.docstatus == 1:
|
||||||
if outstanding_amount > 0 and due_date < nowdate:
|
if self.is_internal_transfer():
|
||||||
|
self.status = 'Internal Transfer'
|
||||||
|
elif outstanding_amount > 0 and due_date < nowdate:
|
||||||
self.status = "Overdue"
|
self.status = "Overdue"
|
||||||
elif outstanding_amount > 0 and due_date >= nowdate:
|
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||||
self.status = "Unpaid"
|
self.status = "Unpaid"
|
||||||
|
|||||||
@ -4,23 +4,25 @@
|
|||||||
// render
|
// render
|
||||||
frappe.listview_settings['Purchase Invoice'] = {
|
frappe.listview_settings['Purchase Invoice'] = {
|
||||||
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||||
"currency", "is_return", "release_date", "on_hold"],
|
"currency", "is_return", "release_date", "on_hold", "represents_company", "is_internal_supplier"],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
if( (flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
|
if ((flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
|
||||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"];
|
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"];
|
||||||
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
} else if (flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
||||||
if(cint(doc.on_hold) && !doc.release_date) {
|
if(cint(doc.on_hold) && !doc.release_date) {
|
||||||
return [__("On Hold"), "darkgrey"];
|
return [__("On Hold"), "darkgrey"];
|
||||||
} else if(cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) {
|
} else if (cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) {
|
||||||
return [__("Temporarily on Hold"), "darkgrey"];
|
return [__("Temporarily on Hold"), "darkgrey"];
|
||||||
} else if(frappe.datetime.get_diff(doc.due_date) < 0) {
|
} else if (frappe.datetime.get_diff(doc.due_date) < 0) {
|
||||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
||||||
} else {
|
} else {
|
||||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
|
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
|
||||||
}
|
}
|
||||||
} else if(cint(doc.is_return)) {
|
} else if (cint(doc.is_return)) {
|
||||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||||
} else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
} else if (doc.company == doc.represents_company && doc.is_internal_supplier) {
|
||||||
|
return [__("Internal Transfer"), "darkgrey", "outstanding_amount,=,0"];
|
||||||
|
} else if (flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
||||||
return [__("Paid"), "green", "outstanding_amount,=,0"];
|
return [__("Paid"), "green", "outstanding_amount,=,0"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,92 +1,38 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2016-07-27 17:24:24.956896",
|
"creation": "2016-07-27 17:24:24.956896",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"company",
|
||||||
|
"account"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
"options": "Company"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.",
|
"description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.",
|
||||||
"fieldname": "default_account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Default Account",
|
"label": "Account",
|
||||||
"length": 0,
|
"options": "Account"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Account",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2016-09-02 07:49:06.567389",
|
"modified": "2020-10-18 17:57:57.110257",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Salary Component Account",
|
"name": "Salary Component Account",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC"
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@ -580,6 +580,16 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("unrealized_profit_loss_account", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company,
|
||||||
|
is_group: 0,
|
||||||
|
root_type: "Liability",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
frm.custom_make_buttons = {
|
frm.custom_make_buttons = {
|
||||||
'Delivery Note': 'Delivery',
|
'Delivery Note': 'Delivery',
|
||||||
'Sales Invoice': 'Sales Return',
|
'Sales Invoice': 'Sales Return',
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_auto_repeat": 1,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-24 19:29:05",
|
"creation": "2013-05-24 19:29:05",
|
||||||
@ -158,6 +157,7 @@
|
|||||||
"more_information",
|
"more_information",
|
||||||
"inter_company_invoice_reference",
|
"inter_company_invoice_reference",
|
||||||
"is_internal_customer",
|
"is_internal_customer",
|
||||||
|
"represents_company",
|
||||||
"customer_group",
|
"customer_group",
|
||||||
"campaign",
|
"campaign",
|
||||||
"is_discounted",
|
"is_discounted",
|
||||||
@ -171,6 +171,7 @@
|
|||||||
"c_form_applicable",
|
"c_form_applicable",
|
||||||
"c_form_no",
|
"c_form_no",
|
||||||
"column_break8",
|
"column_break8",
|
||||||
|
"unrealized_profit_loss_account",
|
||||||
"remarks",
|
"remarks",
|
||||||
"sales_team_section_break",
|
"sales_team_section_break",
|
||||||
"sales_partner",
|
"sales_partner",
|
||||||
@ -1655,7 +1656,7 @@
|
|||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled",
|
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -1950,13 +1951,31 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Company Tax ID",
|
"label": "Company Tax ID",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.is_internal_customer",
|
||||||
|
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
||||||
|
"fieldname": "unrealized_profit_loss_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Unrealized Profit / Loss Account",
|
||||||
|
"options": "Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.is_internal_customer",
|
||||||
|
"description": "Company which internal customer represents",
|
||||||
|
"fetch_from": "customer.represents_company",
|
||||||
|
"fieldname": "represents_company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Represents Company",
|
||||||
|
"options": "Company",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 181,
|
"idx": 181,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-30 13:57:45.086303",
|
"modified": "2020-12-11 12:48:31.769958",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@ -405,6 +405,8 @@ class SalesInvoice(SellingController):
|
|||||||
from erpnext.stock.get_item_details import get_pos_profile_item_details, get_pos_profile
|
from erpnext.stock.get_item_details import get_pos_profile_item_details, get_pos_profile
|
||||||
if not self.pos_profile:
|
if not self.pos_profile:
|
||||||
pos_profile = get_pos_profile(self.company) or {}
|
pos_profile = get_pos_profile(self.company) or {}
|
||||||
|
if not pos_profile:
|
||||||
|
frappe.throw(_("No POS Profile found. Please create a New POS Profile first"))
|
||||||
self.pos_profile = pos_profile.get('name')
|
self.pos_profile = pos_profile.get('name')
|
||||||
|
|
||||||
pos = {}
|
pos = {}
|
||||||
@ -472,6 +474,11 @@ class SalesInvoice(SellingController):
|
|||||||
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
|
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
|
||||||
|
|
||||||
def validate_debit_to_acc(self):
|
def validate_debit_to_acc(self):
|
||||||
|
if not self.debit_to:
|
||||||
|
self.debit_to = get_party_account("Customer", self.customer, self.company)
|
||||||
|
if not self.debit_to:
|
||||||
|
self.raise_missing_debit_credit_account_error("Customer", self.customer)
|
||||||
|
|
||||||
account = frappe.get_cached_value("Account", self.debit_to,
|
account = frappe.get_cached_value("Account", self.debit_to,
|
||||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||||
|
|
||||||
@ -751,6 +758,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.make_customer_gl_entry(gl_entries)
|
self.make_customer_gl_entry(gl_entries)
|
||||||
|
|
||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
|
self.make_internal_transfer_gl_entries(gl_entries)
|
||||||
|
|
||||||
self.make_item_gl_entries(gl_entries)
|
self.make_item_gl_entries(gl_entries)
|
||||||
|
|
||||||
@ -770,7 +778,7 @@ class SalesInvoice(SellingController):
|
|||||||
# Checked both rounding_adjustment and rounded_total
|
# Checked both rounding_adjustment and rounded_total
|
||||||
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
||||||
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
||||||
if grand_total:
|
if grand_total and not self.is_internal_transfer():
|
||||||
# Didnot use base_grand_total to book rounding loss gle
|
# Didnot use base_grand_total to book rounding loss gle
|
||||||
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
||||||
self.precision("grand_total"))
|
self.precision("grand_total"))
|
||||||
@ -809,6 +817,18 @@ class SalesInvoice(SellingController):
|
|||||||
}, account_currency, item=tax)
|
}, account_currency, item=tax)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def make_internal_transfer_gl_entries(self, gl_entries):
|
||||||
|
if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
|
||||||
|
account_currency = get_account_currency(self.unrealized_profit_loss_account)
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": self.unrealized_profit_loss_account,
|
||||||
|
"against": self.customer,
|
||||||
|
"debit": flt(self.total_taxes_and_charges),
|
||||||
|
"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
|
||||||
|
"cost_center": self.cost_center
|
||||||
|
}, account_currency, item=self))
|
||||||
|
|
||||||
def make_item_gl_entries(self, gl_entries):
|
def make_item_gl_entries(self, gl_entries):
|
||||||
# income account gl entries
|
# income account gl entries
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
@ -831,6 +851,8 @@ class SalesInvoice(SellingController):
|
|||||||
asset.db_set("disposal_date", self.posting_date)
|
asset.db_set("disposal_date", self.posting_date)
|
||||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||||
else:
|
else:
|
||||||
|
# Do not book income for transfer within same company
|
||||||
|
if not self.is_internal_transfer():
|
||||||
income_account = (item.income_account
|
income_account = (item.income_account
|
||||||
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
|
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
|
||||||
|
|
||||||
@ -1258,7 +1280,9 @@ class SalesInvoice(SellingController):
|
|||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
status = "Cancelled"
|
status = "Cancelled"
|
||||||
elif self.docstatus == 1:
|
elif self.docstatus == 1:
|
||||||
if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
|
if self.is_internal_transfer():
|
||||||
|
self.status = 'Internal Transfer'
|
||||||
|
elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
|
||||||
self.status = "Overdue and Discounted"
|
self.status = "Overdue and Discounted"
|
||||||
elif outstanding_amount > 0 and due_date < nowdate:
|
elif outstanding_amount > 0 and due_date < nowdate:
|
||||||
self.status = "Overdue"
|
self.status = "Overdue"
|
||||||
@ -1523,9 +1547,13 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
|||||||
if doctype in ["Sales Invoice", "Sales Order"]:
|
if doctype in ["Sales Invoice", "Sales Order"]:
|
||||||
source_doc = frappe.get_doc(doctype, source_name)
|
source_doc = frappe.get_doc(doctype, source_name)
|
||||||
target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order"
|
target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order"
|
||||||
|
source_document_warehouse_field = 'target_warehouse'
|
||||||
|
target_document_warehouse_field = 'from_warehouse'
|
||||||
else:
|
else:
|
||||||
source_doc = frappe.get_doc(doctype, source_name)
|
source_doc = frappe.get_doc(doctype, source_name)
|
||||||
target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order"
|
target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order"
|
||||||
|
source_document_warehouse_field = 'from_warehouse'
|
||||||
|
target_document_warehouse_field = 'target_warehouse'
|
||||||
|
|
||||||
validate_inter_company_transaction(source_doc, doctype)
|
validate_inter_company_transaction(source_doc, doctype)
|
||||||
details = get_inter_company_details(source_doc, doctype)
|
details = get_inter_company_details(source_doc, doctype)
|
||||||
@ -1552,15 +1580,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
|||||||
if currency:
|
if currency:
|
||||||
target_doc.currency = currency
|
target_doc.currency = currency
|
||||||
|
|
||||||
doclist = get_mapped_doc(doctype, source_name, {
|
item_field_map = {
|
||||||
doctype: {
|
|
||||||
"doctype": target_doctype,
|
|
||||||
"postprocess": update_details,
|
|
||||||
"field_no_map": [
|
|
||||||
"taxes_and_charges"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
doctype +" Item": {
|
|
||||||
"doctype": target_doctype + " Item",
|
"doctype": target_doctype + " Item",
|
||||||
"field_no_map": [
|
"field_no_map": [
|
||||||
"income_account",
|
"income_account",
|
||||||
@ -1570,6 +1590,26 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if source_doc.get('update_stock'):
|
||||||
|
item_field_map.update({
|
||||||
|
'field_map': {
|
||||||
|
source_document_warehouse_field: target_document_warehouse_field,
|
||||||
|
'batch_no': 'batch_no',
|
||||||
|
'serial_no': 'serial_no'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
doclist = get_mapped_doc(doctype, source_name, {
|
||||||
|
doctype: {
|
||||||
|
"doctype": target_doctype,
|
||||||
|
"postprocess": update_details,
|
||||||
|
"field_no_map": [
|
||||||
|
"taxes_and_charges"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
doctype +" Item": item_field_map
|
||||||
|
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|||||||
@ -14,8 +14,8 @@ frappe.listview_settings['Sales Invoice'] = {
|
|||||||
"Credit Note Issued": "darkgrey",
|
"Credit Note Issued": "darkgrey",
|
||||||
"Unpaid and Discounted": "orange",
|
"Unpaid and Discounted": "orange",
|
||||||
"Overdue and Discounted": "red",
|
"Overdue and Discounted": "red",
|
||||||
"Overdue": "red"
|
"Overdue": "red",
|
||||||
|
"Internal Transfer": "darkgrey"
|
||||||
};
|
};
|
||||||
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
|
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
|
||||||
},
|
},
|
||||||
|
|||||||
@ -690,7 +690,8 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertTrue(gle)
|
self.assertTrue(gle)
|
||||||
|
|
||||||
def test_pos_gl_entry_with_perpetual_inventory(self):
|
def test_pos_gl_entry_with_perpetual_inventory(self):
|
||||||
make_pos_profile()
|
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||||
|
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||||
|
|
||||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||||
|
|
||||||
@ -746,7 +747,8 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(pos_return.get('payments')[0].amount, -1000)
|
self.assertEqual(pos_return.get('payments')[0].amount, -1000)
|
||||||
|
|
||||||
def test_pos_change_amount(self):
|
def test_pos_change_amount(self):
|
||||||
make_pos_profile()
|
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||||
|
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||||
|
|
||||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
|
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
|
||||||
item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
|
item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
|
||||||
@ -1779,6 +1781,60 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(target_doc.company, "_Test Company 1")
|
self.assertEqual(target_doc.company, "_Test Company 1")
|
||||||
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
||||||
|
|
||||||
|
def test_internal_transfer_gl_entry(self):
|
||||||
|
## Create internal transfer account
|
||||||
|
account = create_account(account_name="Unrealized Profit",
|
||||||
|
parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
|
||||||
|
|
||||||
|
frappe.db.set_value('Company', '_Test Company with perpetual inventory',
|
||||||
|
'unrealized_profit_loss_account', account)
|
||||||
|
|
||||||
|
customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
|
||||||
|
"_Test Company with perpetual inventory")
|
||||||
|
|
||||||
|
create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
|
||||||
|
"_Test Company with perpetual inventory")
|
||||||
|
|
||||||
|
si = create_sales_invoice(
|
||||||
|
company = "_Test Company with perpetual inventory",
|
||||||
|
customer = customer,
|
||||||
|
debit_to = "Debtors - TCP1",
|
||||||
|
warehouse = "Stores - TCP1",
|
||||||
|
income_account = "Sales - TCP1",
|
||||||
|
expense_account = "Cost of Goods Sold - TCP1",
|
||||||
|
cost_center = "Main - TCP1",
|
||||||
|
currency = "INR",
|
||||||
|
do_not_save = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
si.selling_price_list = "_Test Price List Rest of the World"
|
||||||
|
si.update_stock = 1
|
||||||
|
si.items[0].target_warehouse = 'Work In Progress - TCP1'
|
||||||
|
add_taxes(si)
|
||||||
|
si.save()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
target_doc = make_inter_company_transaction("Sales Invoice", si.name)
|
||||||
|
target_doc.company = '_Test Company with perpetual inventory'
|
||||||
|
target_doc.items[0].warehouse = 'Finished Goods - TCP1'
|
||||||
|
add_taxes(target_doc)
|
||||||
|
target_doc.save()
|
||||||
|
target_doc.submit()
|
||||||
|
|
||||||
|
si_gl_entries = [
|
||||||
|
["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()],
|
||||||
|
["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()]
|
||||||
|
]
|
||||||
|
|
||||||
|
check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
|
||||||
|
|
||||||
|
pi_gl_entries = [
|
||||||
|
["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()],
|
||||||
|
["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()]
|
||||||
|
]
|
||||||
|
|
||||||
|
check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
|
||||||
|
|
||||||
def test_eway_bill_json(self):
|
def test_eway_bill_json(self):
|
||||||
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
||||||
address = frappe.get_doc({
|
address = frappe.get_doc({
|
||||||
@ -2038,3 +2094,56 @@ def get_taxes_and_charges():
|
|||||||
"rate": 2,
|
"rate": 2,
|
||||||
"row_id": 1
|
"row_id": 1
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
def create_internal_customer(customer_name, represents_company, allowed_to_interact_with):
|
||||||
|
if not frappe.db.exists("Customer", customer_name):
|
||||||
|
customer = frappe.get_doc({
|
||||||
|
"customer_group": "_Test Customer Group",
|
||||||
|
"customer_name": customer_name,
|
||||||
|
"customer_type": "Individual",
|
||||||
|
"doctype": "Customer",
|
||||||
|
"territory": "_Test Territory",
|
||||||
|
"is_internal_customer": 1,
|
||||||
|
"represents_company": represents_company
|
||||||
|
})
|
||||||
|
|
||||||
|
customer.append("companies", {
|
||||||
|
"company": allowed_to_interact_with
|
||||||
|
})
|
||||||
|
|
||||||
|
customer.insert()
|
||||||
|
customer_name = customer.name
|
||||||
|
else:
|
||||||
|
customer_name = frappe.db.get_value("Customer", customer_name)
|
||||||
|
|
||||||
|
return customer_name
|
||||||
|
|
||||||
|
def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
|
||||||
|
if not frappe.db.exists("Supplier", supplier_name):
|
||||||
|
supplier = frappe.get_doc({
|
||||||
|
"supplier_group": "_Test Supplier Group",
|
||||||
|
"supplier_name": supplier_name,
|
||||||
|
"doctype": "Supplier",
|
||||||
|
"is_internal_supplier": 1,
|
||||||
|
"represents_company": represents_company
|
||||||
|
})
|
||||||
|
|
||||||
|
supplier.append("companies", {
|
||||||
|
"company": allowed_to_interact_with
|
||||||
|
})
|
||||||
|
|
||||||
|
supplier.insert()
|
||||||
|
supplier_name = supplier.name
|
||||||
|
else:
|
||||||
|
supplier_name = frappe.db.exists("Supplier", supplier_name)
|
||||||
|
|
||||||
|
return supplier_name
|
||||||
|
|
||||||
|
def add_taxes(doc):
|
||||||
|
doc.append('taxes', {
|
||||||
|
'account_head': '_Test Account Excise Duty - TCP1',
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"cost_center": "Main - TCP1",
|
||||||
|
"description": "Excise Duty",
|
||||||
|
"rate": 12
|
||||||
|
})
|
||||||
@ -42,11 +42,13 @@
|
|||||||
|
|
||||||
{% if(filters.show_future_payments) { %}
|
{% if(filters.show_future_payments) { %}
|
||||||
{% var balance_row = data.slice(-1).pop();
|
{% var balance_row = data.slice(-1).pop();
|
||||||
var range1 = report.columns[11].label;
|
var start = filters.based_on_payment_terms ? 13 : 11;
|
||||||
var range2 = report.columns[12].label;
|
var range1 = report.columns[start].label;
|
||||||
var range3 = report.columns[13].label;
|
var range2 = report.columns[start+1].label;
|
||||||
var range4 = report.columns[14].label;
|
var range3 = report.columns[start+2].label;
|
||||||
var range5 = report.columns[15].label;
|
var range4 = report.columns[start+3].label;
|
||||||
|
var range5 = report.columns[start+4].label;
|
||||||
|
var range6 = report.columns[start+5].label;
|
||||||
%}
|
%}
|
||||||
{% if(balance_row) { %}
|
{% if(balance_row) { %}
|
||||||
<table class="table table-bordered table-condensed">
|
<table class="table table-bordered table-condensed">
|
||||||
@ -70,17 +72,31 @@
|
|||||||
<th>{%= __(range3) %}</th>
|
<th>{%= __(range3) %}</th>
|
||||||
<th>{%= __(range4) %}</th>
|
<th>{%= __(range4) %}</th>
|
||||||
<th>{%= __(range5) %}</th>
|
<th>{%= __(range5) %}</th>
|
||||||
|
<th>{%= __(range6) %}</th>
|
||||||
<th>{%= __("Total") %}</th>
|
<th>{%= __("Total") %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{%= __("Total Outstanding") %}</td>
|
<td>{%= __("Total Outstanding") %}</td>
|
||||||
<td class="text-right">{%= format_number(balance_row["range1"], null, 2) %}</td>
|
<td class="text-right">
|
||||||
<td class="text-right">{%= format_currency(balance_row["range2"]) %}</td>
|
{%= format_number(balance_row["age"], null, 2) %}
|
||||||
<td class="text-right">{%= format_currency(balance_row["range3"]) %}</td>
|
</td>
|
||||||
<td class="text-right">{%= format_currency(balance_row["range4"]) %}</td>
|
<td class="text-right">
|
||||||
<td class="text-right">{%= format_currency(balance_row["range5"]) %}</td>
|
{%= format_currency(balance_row["range1"], data[data.length-1]["currency"]) %}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{%= format_currency(balance_row["range2"], data[data.length-1]["currency"]) %}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{%= format_currency(balance_row["range3"], data[data.length-1]["currency"]) %}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{%= format_currency(balance_row["range4"], data[data.length-1]["currency"]) %}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{%= format_currency(balance_row["range5"], data[data.length-1]["currency"]) %}
|
||||||
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %}
|
{%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %}
|
||||||
</td>
|
</td>
|
||||||
@ -91,6 +107,7 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
<td></td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
|
{%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
|
||||||
</td>
|
</td>
|
||||||
@ -101,6 +118,7 @@
|
|||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
|
<th></th>
|
||||||
<th class="text-right">
|
<th class="text-right">
|
||||||
{%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}</th>
|
{%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -218,15 +236,15 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{%= format_currency(data[i]["invoiced"], data[0]["currency"] ) %}</td>
|
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td>
|
||||||
|
|
||||||
{% if(!filters.show_future_payments) { %}
|
{% if(!filters.show_future_payments) { %}
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
|
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %} </td>
|
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td>
|
||||||
{% } %}
|
{% } %}
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
|
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||||
|
|
||||||
{% if(filters.show_future_payments) { %}
|
{% if(filters.show_future_payments) { %}
|
||||||
{% if(report.report_name === "Accounts Receivable") { %}
|
{% if(report.report_name === "Accounts Receivable") { %}
|
||||||
@ -234,8 +252,8 @@
|
|||||||
{%= data[i]["po_no"] %}</td>
|
{%= data[i]["po_no"] %}</td>
|
||||||
{% } %}
|
{% } %}
|
||||||
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[0]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[0]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
|
||||||
{% } %}
|
{% } %}
|
||||||
{% } %}
|
{% } %}
|
||||||
{% } else { %}
|
{% } else { %}
|
||||||
@ -256,10 +274,10 @@
|
|||||||
{% } else { %}
|
{% } else { %}
|
||||||
<td><b>{%= __("Total") %}</b></td>
|
<td><b>{%= __("Total") %}</b></td>
|
||||||
{% } %}
|
{% } %}
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[0]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[0]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[0]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
|
||||||
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[0]["currency"]) %}</td>
|
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||||
{% } %}
|
{% } %}
|
||||||
{% } %}
|
{% } %}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -14,11 +14,93 @@ def execute(filters=None):
|
|||||||
|
|
||||||
def get_column():
|
def get_column():
|
||||||
return [
|
return [
|
||||||
_("Delivery Note") + ":Link/Delivery Note:120", _("Status") + "::120", _("Date") + ":Date:100",
|
{
|
||||||
_("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120",
|
"label": _("Delivery Note"),
|
||||||
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
|
"fieldname": "name",
|
||||||
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",
|
"fieldtype": "Link",
|
||||||
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
|
"options": "Delivery Note",
|
||||||
|
"width": 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Date"),
|
||||||
|
"fieldname": "date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer"),
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Customer",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer Name"),
|
||||||
|
"fieldname": "customer_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Code"),
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Amount"),
|
||||||
|
"fieldname": "amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Billed Amount"),
|
||||||
|
"fieldname": "billed_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Returned Amount"),
|
||||||
|
"fieldname": "returned_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Pending Amount"),
|
||||||
|
"fieldname": "pending_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Name"),
|
||||||
|
"fieldname": "item_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Description"),
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Project"),
|
||||||
|
"fieldname": "project",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Project",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Company"),
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"width": 120
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_args():
|
def get_args():
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from frappe.utils import flt
|
|||||||
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts,
|
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts,
|
||||||
get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row,
|
get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row,
|
||||||
get_group_by_conditions)
|
get_group_by_conditions)
|
||||||
|
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
return _execute(filters)
|
return _execute(filters)
|
||||||
@ -22,7 +23,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
aii_account_map = get_aii_accounts()
|
aii_account_map = get_aii_accounts()
|
||||||
if item_list:
|
if item_list:
|
||||||
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency,
|
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency,
|
||||||
doctype="Purchase Invoice", tax_doctype="Purchase Taxes and Charges")
|
doctype='Purchase Invoice', tax_doctype='Purchase Taxes and Charges')
|
||||||
|
|
||||||
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
|
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
|
||||||
|
|
||||||
@ -34,10 +35,14 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
if filters.get('group_by'):
|
if filters.get('group_by'):
|
||||||
grand_total = get_grand_total(filters, 'Purchase Invoice')
|
grand_total = get_grand_total(filters, 'Purchase Invoice')
|
||||||
|
|
||||||
|
item_details = get_item_details()
|
||||||
|
|
||||||
for d in item_list:
|
for d in item_list:
|
||||||
if not d.stock_qty:
|
if not d.stock_qty:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
item_record = item_details.get(d.item_code)
|
||||||
|
|
||||||
purchase_receipt = None
|
purchase_receipt = None
|
||||||
if d.purchase_receipt:
|
if d.purchase_receipt:
|
||||||
purchase_receipt = d.purchase_receipt
|
purchase_receipt = d.purchase_receipt
|
||||||
@ -48,8 +53,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
|
|
||||||
row = {
|
row = {
|
||||||
'item_code': d.item_code,
|
'item_code': d.item_code,
|
||||||
'item_name': d.item_name,
|
'item_name': item_record.item_name,
|
||||||
'item_group': d.item_group,
|
'item_group': item_record.item_group,
|
||||||
'description': d.description,
|
'description': d.description,
|
||||||
'invoice': d.parent,
|
'invoice': d.parent,
|
||||||
'posting_date': d.posting_date,
|
'posting_date': d.posting_date,
|
||||||
@ -81,10 +86,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
for tax in tax_columns:
|
for tax in tax_columns:
|
||||||
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
||||||
row.update({
|
row.update({
|
||||||
frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0),
|
frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0),
|
||||||
frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0),
|
frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0),
|
||||||
})
|
})
|
||||||
total_tax += flt(item_tax.get("tax_amount"))
|
total_tax += flt(item_tax.get('tax_amount'))
|
||||||
|
|
||||||
row.update({
|
row.update({
|
||||||
'total_tax': total_tax,
|
'total_tax': total_tax,
|
||||||
@ -309,8 +314,8 @@ def get_items(filters, additional_query_columns):
|
|||||||
select
|
select
|
||||||
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
|
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
|
||||||
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
|
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
|
||||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice Item`.`item_code`,
|
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
|
||||||
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`, `tabPurchase Invoice Item`.description,
|
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
|
||||||
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||||
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
||||||
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from frappe.utils import flt, cstr
|
|||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
from frappe.utils.xlsxutils import handle_html
|
from frappe.utils.xlsxutils import handle_html
|
||||||
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
||||||
|
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details, get_customer_details
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
return _execute(filters)
|
return _execute(filters)
|
||||||
@ -16,7 +17,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
if not filters: filters = {}
|
if not filters: filters = {}
|
||||||
columns = get_columns(additional_table_columns, filters)
|
columns = get_columns(additional_table_columns, filters)
|
||||||
|
|
||||||
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
|
company_currency = frappe.get_cached_value('Company', filters.get('company'), 'default_currency')
|
||||||
|
|
||||||
item_list = get_items(filters, additional_query_columns)
|
item_list = get_items(filters, additional_query_columns)
|
||||||
if item_list:
|
if item_list:
|
||||||
@ -33,7 +34,13 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
if filters.get('group_by'):
|
if filters.get('group_by'):
|
||||||
grand_total = get_grand_total(filters, 'Sales Invoice')
|
grand_total = get_grand_total(filters, 'Sales Invoice')
|
||||||
|
|
||||||
|
customer_details = get_customer_details()
|
||||||
|
item_details = get_item_details()
|
||||||
|
|
||||||
for d in item_list:
|
for d in item_list:
|
||||||
|
customer_record = customer_details.get(d.customer)
|
||||||
|
item_record = item_details.get(d.item_code)
|
||||||
|
|
||||||
delivery_note = None
|
delivery_note = None
|
||||||
if d.delivery_note:
|
if d.delivery_note:
|
||||||
delivery_note = d.delivery_note
|
delivery_note = d.delivery_note
|
||||||
@ -45,14 +52,14 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
|
|
||||||
row = {
|
row = {
|
||||||
'item_code': d.item_code,
|
'item_code': d.item_code,
|
||||||
'item_name': d.item_name,
|
'item_name': item_record.item_name,
|
||||||
'item_group': d.item_group,
|
'item_group': item_record.item_group,
|
||||||
'description': d.description,
|
'description': d.description,
|
||||||
'invoice': d.parent,
|
'invoice': d.parent,
|
||||||
'posting_date': d.posting_date,
|
'posting_date': d.posting_date,
|
||||||
'customer': d.customer,
|
'customer': d.customer,
|
||||||
'customer_name': d.customer_name,
|
'customer_name': customer_record.customer_name,
|
||||||
'customer_group': d.customer_group,
|
'customer_group': customer_record.customer_group,
|
||||||
}
|
}
|
||||||
|
|
||||||
if additional_query_columns:
|
if additional_query_columns:
|
||||||
@ -90,10 +97,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
for tax in tax_columns:
|
for tax in tax_columns:
|
||||||
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
|
||||||
row.update({
|
row.update({
|
||||||
frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0),
|
frappe.scrub(tax + ' Rate'): item_tax.get('tax_rate', 0),
|
||||||
frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0),
|
frappe.scrub(tax + ' Amount'): item_tax.get('tax_amount', 0),
|
||||||
})
|
})
|
||||||
total_tax += flt(item_tax.get("tax_amount"))
|
total_tax += flt(item_tax.get('tax_amount'))
|
||||||
|
|
||||||
row.update({
|
row.update({
|
||||||
'total_tax': total_tax,
|
'total_tax': total_tax,
|
||||||
@ -226,7 +233,7 @@ def get_columns(additional_table_columns, filters):
|
|||||||
if filters.get('group_by') != 'Territory':
|
if filters.get('group_by') != 'Territory':
|
||||||
columns.extend([
|
columns.extend([
|
||||||
{
|
{
|
||||||
'label': _("Territory"),
|
'label': _('Territory'),
|
||||||
'fieldname': 'territory',
|
'fieldname': 'territory',
|
||||||
'fieldtype': 'Link',
|
'fieldtype': 'Link',
|
||||||
'options': 'Territory',
|
'options': 'Territory',
|
||||||
@ -374,13 +381,12 @@ def get_items(filters, additional_query_columns):
|
|||||||
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
||||||
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||||
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.item_name,
|
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
||||||
`tabSales Invoice Item`.item_group, `tabSales Invoice Item`.description, `tabSales Invoice Item`.sales_order,
|
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,
|
||||||
`tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.income_account,
|
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
|
||||||
`tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.stock_qty,
|
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
|
||||||
`tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate,
|
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount,
|
||||||
`tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name,
|
`tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
||||||
`tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
|
||||||
`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
|
`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
|
||||||
from `tabSales Invoice`, `tabSales Invoice Item`
|
from `tabSales Invoice`, `tabSales Invoice Item`
|
||||||
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
|
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
|
||||||
@ -417,14 +423,14 @@ def get_deducted_taxes():
|
|||||||
return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'")
|
return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'")
|
||||||
|
|
||||||
def get_tax_accounts(item_list, columns, company_currency,
|
def get_tax_accounts(item_list, columns, company_currency,
|
||||||
doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
|
doctype='Sales Invoice', tax_doctype='Sales Taxes and Charges'):
|
||||||
import json
|
import json
|
||||||
item_row_map = {}
|
item_row_map = {}
|
||||||
tax_columns = []
|
tax_columns = []
|
||||||
invoice_item_row = {}
|
invoice_item_row = {}
|
||||||
itemised_tax = {}
|
itemised_tax = {}
|
||||||
|
|
||||||
tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field("tax_amount"),
|
tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field('tax_amount'),
|
||||||
currency=company_currency) or 2
|
currency=company_currency) or 2
|
||||||
|
|
||||||
for d in item_list:
|
for d in item_list:
|
||||||
@ -469,8 +475,8 @@ def get_tax_accounts(item_list, columns, company_currency,
|
|||||||
tax_rate = tax_data
|
tax_rate = tax_data
|
||||||
tax_amount = 0
|
tax_amount = 0
|
||||||
|
|
||||||
if charge_type == "Actual" and not tax_rate:
|
if charge_type == 'Actual' and not tax_rate:
|
||||||
tax_rate = "NA"
|
tax_rate = 'NA'
|
||||||
|
|
||||||
item_net_amount = sum([flt(d.base_net_amount)
|
item_net_amount = sum([flt(d.base_net_amount)
|
||||||
for d in item_row_map.get(parent, {}).get(item_code, [])])
|
for d in item_row_map.get(parent, {}).get(item_code, [])])
|
||||||
@ -484,17 +490,17 @@ def get_tax_accounts(item_list, columns, company_currency,
|
|||||||
if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value)
|
if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value)
|
||||||
|
|
||||||
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
|
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
|
||||||
"tax_rate": tax_rate,
|
'tax_rate': tax_rate,
|
||||||
"tax_amount": tax_value
|
'tax_amount': tax_value
|
||||||
})
|
})
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
continue
|
||||||
elif charge_type == "Actual" and tax_amount:
|
elif charge_type == 'Actual' and tax_amount:
|
||||||
for d in invoice_item_row.get(parent, []):
|
for d in invoice_item_row.get(parent, []):
|
||||||
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
|
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
|
||||||
"tax_rate": "NA",
|
'tax_rate': 'NA',
|
||||||
"tax_amount": flt((tax_amount * d.base_net_amount) / d.base_net_total,
|
'tax_amount': flt((tax_amount * d.base_net_amount) / d.base_net_total,
|
||||||
tax_amount_precision)
|
tax_amount_precision)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -563,7 +569,7 @@ def add_total_row(data, filters, prev_group_by_value, item, total_row_map,
|
|||||||
})
|
})
|
||||||
|
|
||||||
total_row_map.setdefault('total_row', {
|
total_row_map.setdefault('total_row', {
|
||||||
subtotal_display_field: "Total",
|
subtotal_display_field: 'Total',
|
||||||
'stock_qty': 0.0,
|
'stock_qty': 0.0,
|
||||||
'amount': 0.0,
|
'amount': 0.0,
|
||||||
'bold': 1,
|
'bold': 1,
|
||||||
|
|||||||
@ -17,18 +17,26 @@ def get_ordered_to_be_billed_data(args):
|
|||||||
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
Select
|
Select
|
||||||
`{parent_tab}`.name, `{parent_tab}`.status, `{parent_tab}`.{date_field}, `{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
|
`{parent_tab}`.name, `{parent_tab}`.{date_field},
|
||||||
{project_field}, `{child_tab}`.item_code, `{child_tab}`.base_amount,
|
`{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
|
||||||
|
`{child_tab}`.item_code,
|
||||||
|
`{child_tab}`.base_amount,
|
||||||
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
|
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
|
||||||
(`{child_tab}`.base_amount - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1))),
|
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0)),
|
||||||
`{child_tab}`.item_name, `{child_tab}`.description, `{parent_tab}`.company
|
(`{child_tab}`.base_amount -
|
||||||
|
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)) -
|
||||||
|
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))),
|
||||||
|
`{child_tab}`.item_name, `{child_tab}`.description,
|
||||||
|
{project_field}, `{parent_tab}`.company
|
||||||
from
|
from
|
||||||
`{parent_tab}`, `{child_tab}`
|
`{parent_tab}`, `{child_tab}`
|
||||||
where
|
where
|
||||||
`{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1
|
`{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1
|
||||||
and `{parent_tab}`.status not in ('Closed', 'Completed')
|
and `{parent_tab}`.status not in ('Closed', 'Completed')
|
||||||
and `{child_tab}`.amount > 0 and round(`{child_tab}`.billed_amt *
|
and `{child_tab}`.amount > 0
|
||||||
ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) < `{child_tab}`.base_amount
|
and (`{child_tab}`.base_amount -
|
||||||
|
round(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) -
|
||||||
|
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))) > 0
|
||||||
order by
|
order by
|
||||||
`{parent_tab}`.{order} {order_by}
|
`{parent_tab}`.{order} {order_by}
|
||||||
""".format(parent_tab = 'tab' + doctype, child_tab = 'tab' + child_tab, precision= precision, party = party,
|
""".format(parent_tab = 'tab' + doctype, child_tab = 'tab' + child_tab, precision= precision, party = party,
|
||||||
|
|||||||
@ -14,11 +14,93 @@ def execute(filters=None):
|
|||||||
|
|
||||||
def get_column():
|
def get_column():
|
||||||
return [
|
return [
|
||||||
_("Purchase Receipt") + ":Link/Purchase Receipt:120", _("Status") + "::120", _("Date") + ":Date:100",
|
{
|
||||||
_("Supplier") + ":Link/Supplier:120", _("Supplier Name") + "::120",
|
"label": _("Purchase Receipt"),
|
||||||
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
|
"fieldname": "name",
|
||||||
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100",
|
"fieldtype": "Link",
|
||||||
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
|
"options": "Purchase Receipt",
|
||||||
|
"width": 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Date"),
|
||||||
|
"fieldname": "date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Supplier"),
|
||||||
|
"fieldname": "supplier",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Supplier",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Supplier Name"),
|
||||||
|
"fieldname": "supplier_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Code"),
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Amount"),
|
||||||
|
"fieldname": "amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Billed Amount"),
|
||||||
|
"fieldname": "billed_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Returned Amount"),
|
||||||
|
"fieldname": "returned_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Pending Amount"),
|
||||||
|
"fieldname": "pending_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Name"),
|
||||||
|
"fieldname": "item_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Description"),
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Project"),
|
||||||
|
"fieldname": "project",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Project",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Company"),
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"width": 120
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_args():
|
def get_args():
|
||||||
|
|||||||
@ -78,7 +78,10 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb
|
|||||||
else:
|
else:
|
||||||
return ((fy.name, fy.year_start_date, fy.year_end_date),)
|
return ((fy.name, fy.year_start_date, fy.year_end_date),)
|
||||||
|
|
||||||
error_msg = _("""{0} {1} not in any active Fiscal Year.""").format(label, formatdate(transaction_date))
|
error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date))
|
||||||
|
if company:
|
||||||
|
error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company))
|
||||||
|
|
||||||
if verbose==1: frappe.msgprint(error_msg)
|
if verbose==1: frappe.msgprint(error_msg)
|
||||||
raise FiscalYearError(error_msg)
|
raise FiscalYearError(error_msg)
|
||||||
|
|
||||||
|
|||||||
@ -75,7 +75,6 @@ def get_data(filters):
|
|||||||
for asset in assets_record:
|
for asset in assets_record:
|
||||||
asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \
|
asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \
|
||||||
- flt(depreciation_amount_map.get(asset.name))
|
- flt(depreciation_amount_map.get(asset.name))
|
||||||
if asset_value:
|
|
||||||
row = {
|
row = {
|
||||||
"asset_id": asset.asset_id,
|
"asset_id": asset.asset_id,
|
||||||
"asset_name": asset.asset_name,
|
"asset_name": asset.asset_name,
|
||||||
|
|||||||
@ -168,6 +168,7 @@
|
|||||||
"bold": 1,
|
"bold": 1,
|
||||||
"fieldname": "supplier",
|
"fieldname": "supplier",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_global_search": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Supplier",
|
"label": "Supplier",
|
||||||
"oldfieldname": "supplier",
|
"oldfieldname": "supplier",
|
||||||
@ -1106,7 +1107,7 @@
|
|||||||
"idx": 105,
|
"idx": 105,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-30 13:58:14.697921",
|
"modified": "2020-12-03 16:46:44.229351",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
|||||||
@ -290,11 +290,17 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}, __("Get Items From"));
|
}, __("Get Items From"));
|
||||||
|
|
||||||
|
// Link Material Requests
|
||||||
|
this.frm.add_custom_button(__('Link to Material Requests'),
|
||||||
|
function() {
|
||||||
|
erpnext.buying.link_to_mrs(me.frm);
|
||||||
|
}, __("Tools"));
|
||||||
|
|
||||||
// Get Suppliers
|
// Get Suppliers
|
||||||
this.frm.add_custom_button(__('Get Suppliers'),
|
this.frm.add_custom_button(__('Get Suppliers'),
|
||||||
function() {
|
function() {
|
||||||
me.get_suppliers_button(me.frm);
|
me.get_suppliers_button(me.frm);
|
||||||
});
|
}, __("Tools"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,6 @@
|
|||||||
"suppliers",
|
"suppliers",
|
||||||
"items_section",
|
"items_section",
|
||||||
"items",
|
"items",
|
||||||
"link_to_mrs",
|
|
||||||
"supplier_response_section",
|
"supplier_response_section",
|
||||||
"salutation",
|
"salutation",
|
||||||
"subject",
|
"subject",
|
||||||
@ -118,13 +117,6 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
|
|
||||||
"fieldname": "link_to_mrs",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"label": "Link to Material Requests"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"depends_on": "eval:!doc.__islocal",
|
|
||||||
"fieldname": "supplier_response_section",
|
"fieldname": "supplier_response_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Email Details"
|
"label": "Email Details"
|
||||||
@ -260,7 +252,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-04 22:04:29.017134",
|
"modified": "2020-11-05 22:04:29.017134",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Request for Quotation",
|
"name": "Request for Quotation",
|
||||||
|
|||||||
@ -49,6 +49,12 @@ class Supplier(TransactionBase):
|
|||||||
msgprint(_("Series is mandatory"), raise_exception=1)
|
msgprint(_("Series is mandatory"), raise_exception=1)
|
||||||
|
|
||||||
validate_party_accounts(self)
|
validate_party_accounts(self)
|
||||||
|
self.validate_internal_supplier()
|
||||||
|
|
||||||
|
def validate_internal_supplier(self):
|
||||||
|
if self.is_internal_supplier and frappe.db.get_value("Supplier", {"represents_company": self.represents_company}, "name"):
|
||||||
|
frappe.throw(_("Internal Supplier for company {0} already exists").format(
|
||||||
|
frappe.bold(self.represents_company)))
|
||||||
|
|
||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
delete_contact_and_address('Supplier', self.name)
|
delete_contact_and_address('Supplier', self.name)
|
||||||
|
|||||||
@ -50,6 +50,12 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
|||||||
})
|
})
|
||||||
}, __("Get Items From"));
|
}, __("Get Items From"));
|
||||||
|
|
||||||
|
// Link Material Requests
|
||||||
|
this.frm.add_custom_button(__('Link to Material Requests'),
|
||||||
|
function() {
|
||||||
|
erpnext.buying.link_to_mrs(me.frm);
|
||||||
|
}, __("Tools"));
|
||||||
|
|
||||||
this.frm.add_custom_button(__("Request for Quotation"),
|
this.frm.add_custom_button(__("Request for Quotation"),
|
||||||
function() {
|
function() {
|
||||||
if (!me.frm.doc.supplier) {
|
if (!me.frm.doc.supplier) {
|
||||||
|
|||||||
@ -35,7 +35,6 @@
|
|||||||
"ignore_pricing_rule",
|
"ignore_pricing_rule",
|
||||||
"items_section",
|
"items_section",
|
||||||
"items",
|
"items",
|
||||||
"link_to_mrs",
|
|
||||||
"pricing_rule_details",
|
"pricing_rule_details",
|
||||||
"pricing_rules",
|
"pricing_rules",
|
||||||
"section_break_22",
|
"section_break_22",
|
||||||
@ -322,12 +321,6 @@
|
|||||||
"options": "Supplier Quotation Item",
|
"options": "Supplier Quotation Item",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
|
|
||||||
"fieldname": "link_to_mrs",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"label": "Link to material requests"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "pricing_rule_details",
|
"fieldname": "pricing_rule_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -806,9 +799,10 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-shopping-cart",
|
"icon": "fa fa-shopping-cart",
|
||||||
"idx": 29,
|
"idx": 29,
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-30 13:58:33.043971",
|
"modified": "2020-12-03 15:18:29.073368",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Supplier Quotation",
|
"name": "Supplier Quotation",
|
||||||
|
|||||||
@ -23,6 +23,8 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import g
|
|||||||
from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
|
from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
|
||||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||||
|
|
||||||
|
class AccountMissingError(frappe.ValidationError): pass
|
||||||
|
|
||||||
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
|
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
|
||||||
|
|
||||||
class AccountsController(TransactionBase):
|
class AccountsController(TransactionBase):
|
||||||
@ -105,6 +107,8 @@ class AccountsController(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
self.validate_deferred_start_and_end_date()
|
self.validate_deferred_start_and_end_date()
|
||||||
|
|
||||||
|
self.set_inter_company_account()
|
||||||
|
|
||||||
validate_regional(self)
|
validate_regional(self)
|
||||||
if self.doctype != 'Material Request':
|
if self.doctype != 'Material Request':
|
||||||
apply_pricing_rule_on_transaction(self)
|
apply_pricing_rule_on_transaction(self)
|
||||||
@ -735,6 +739,21 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
return self._abbr
|
return self._abbr
|
||||||
|
|
||||||
|
def raise_missing_debit_credit_account_error(self, party_type, party):
|
||||||
|
"""Raise an error if debit to/credit to account does not exist."""
|
||||||
|
db_or_cr = frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To")
|
||||||
|
rec_or_pay = "Receivable" if self.doctype == "Sales Invoice" else "Payable"
|
||||||
|
|
||||||
|
link_to_party = frappe.utils.get_link_to_form(party_type, party)
|
||||||
|
link_to_company = frappe.utils.get_link_to_form("Company", self.company)
|
||||||
|
|
||||||
|
message = _("{0} Account not found against Customer {1}.").format(db_or_cr, frappe.bold(party) or '')
|
||||||
|
message += "<br>" + _("Please set one of the following:") + "<br>"
|
||||||
|
message += "<br><ul><li>" + _("'Account' in the Accounting section of Customer {0}").format(link_to_party) + "</li>"
|
||||||
|
message += "<li>" + _("'Default {0} Account' in Company {1}").format(rec_or_pay, link_to_company) + "</li></ul>"
|
||||||
|
|
||||||
|
frappe.throw(message, title=_("Account Missing"), exc=AccountMissingError)
|
||||||
|
|
||||||
def validate_party(self):
|
def validate_party(self):
|
||||||
party_type, party = self.get_party()
|
party_type, party = self.get_party()
|
||||||
validate_party_frozen_disabled(party_type, party)
|
validate_party_frozen_disabled(party_type, party)
|
||||||
@ -915,6 +934,38 @@ class AccountsController(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
return frappe.db.get_single_value("Global Defaults", "disable_rounded_total")
|
return frappe.db.get_single_value("Global Defaults", "disable_rounded_total")
|
||||||
|
|
||||||
|
def set_inter_company_account(self):
|
||||||
|
"""
|
||||||
|
Set intercompany account for inter warehouse transactions
|
||||||
|
This account will be used in case billing company and internal customer's
|
||||||
|
representation company is same
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.is_internal_transfer() and not self.unrealized_profit_loss_account:
|
||||||
|
unrealized_profit_loss_account = frappe.db.get_value('Company', self.company, 'unrealized_profit_loss_account')
|
||||||
|
|
||||||
|
if not unrealized_profit_loss_account:
|
||||||
|
msg = _("Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0}").format(
|
||||||
|
frappe.bold(self.company))
|
||||||
|
frappe.throw(msg)
|
||||||
|
|
||||||
|
self.unrealized_profit_loss_account = unrealized_profit_loss_account
|
||||||
|
|
||||||
|
def is_internal_transfer(self):
|
||||||
|
"""
|
||||||
|
It will an internal transfer if its an internal customer and representation
|
||||||
|
company is same as billing company
|
||||||
|
"""
|
||||||
|
if self.doctype == 'Sales Invoice':
|
||||||
|
internal_party_field = 'is_internal_customer'
|
||||||
|
else:
|
||||||
|
internal_party_field = 'is_internal_supplier'
|
||||||
|
|
||||||
|
if self.get(internal_party_field) and (self.represents_company == self.company):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_tax_rate(account_head):
|
def get_tax_rate(account_head):
|
||||||
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
|
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
|
||||||
|
|||||||
@ -42,6 +42,7 @@ class BuyingController(StockController):
|
|||||||
self.validate_items()
|
self.validate_items()
|
||||||
self.set_qty_as_per_stock_uom()
|
self.set_qty_as_per_stock_uom()
|
||||||
self.validate_stock_or_nonstock_items()
|
self.validate_stock_or_nonstock_items()
|
||||||
|
self.update_tax_category_for_internal_transfer()
|
||||||
self.validate_warehouse()
|
self.validate_warehouse()
|
||||||
self.validate_from_warehouse()
|
self.validate_from_warehouse()
|
||||||
self.set_supplier_address()
|
self.set_supplier_address()
|
||||||
@ -94,13 +95,23 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
def validate_stock_or_nonstock_items(self):
|
def validate_stock_or_nonstock_items(self):
|
||||||
if self.meta.get_field("taxes") and not self.get_stock_items() and not self.get_asset_items():
|
if self.meta.get_field("taxes") and not self.get_stock_items() and not self.get_asset_items():
|
||||||
|
msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items')
|
||||||
|
self.update_tax_category(msg)
|
||||||
|
|
||||||
|
def update_tax_category_for_internal_transfer(self):
|
||||||
|
if self.doctype == 'Purchase Invoice' and self.is_internal_transfer():
|
||||||
|
msg = _('Tax Category has been changed to "Total" as its an internal purchase.')
|
||||||
|
self.update_tax_category(msg)
|
||||||
|
|
||||||
|
def update_tax_category(self, msg):
|
||||||
tax_for_valuation = [d for d in self.get("taxes")
|
tax_for_valuation = [d for d in self.get("taxes")
|
||||||
if d.category in ["Valuation", "Valuation and Total"]]
|
if d.category in ["Valuation", "Valuation and Total"]]
|
||||||
|
|
||||||
if tax_for_valuation:
|
if tax_for_valuation:
|
||||||
for d in tax_for_valuation:
|
for d in tax_for_valuation:
|
||||||
d.category = 'Total'
|
d.category = 'Total'
|
||||||
msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
|
|
||||||
|
msgprint(msg)
|
||||||
|
|
||||||
def validate_asset_return(self):
|
def validate_asset_return(self):
|
||||||
if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return:
|
if self.doctype not in ['Purchase Receipt', 'Purchase Invoice'] or not self.is_return:
|
||||||
@ -497,6 +508,10 @@ class BuyingController(StockController):
|
|||||||
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
||||||
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||||
|
|
||||||
|
if self.doctype=="Purchase Receipt" and d.meta.get_field("received_stock_qty"):
|
||||||
|
# Set Received Qty in Stock UOM
|
||||||
|
d.received_stock_qty = flt(d.received_qty) * flt(d.conversion_factor, d.precision("conversion_factor"))
|
||||||
|
|
||||||
def validate_purchase_return(self):
|
def validate_purchase_return(self):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if self.is_return and flt(d.rejected_qty) != 0:
|
if self.is_return and flt(d.rejected_qty) != 0:
|
||||||
|
|||||||
@ -203,10 +203,37 @@ def get_already_returned_items(doc):
|
|||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
def get_returned_qty_map_for_row(row_name, doctype):
|
||||||
|
child_doctype = doctype + " Item"
|
||||||
|
reference_field = frappe.scrub(child_doctype) if doctype == "Purchase Receipt" else "dn_detail"
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
"sum(abs(`tab{0}`.qty)) as qty".format(child_doctype),
|
||||||
|
"sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype)
|
||||||
|
]
|
||||||
|
|
||||||
|
if doctype == "Purchase Receipt":
|
||||||
|
fields += [
|
||||||
|
"sum(abs(`tab{0}`.rejected_qty)) as rejected_qty".format(child_doctype),
|
||||||
|
"sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype),
|
||||||
|
"sum(abs(`tab{0}`.received_stock_qty)) as received_stock_qty".format(child_doctype)
|
||||||
|
]
|
||||||
|
|
||||||
|
data = frappe.db.get_list(doctype,
|
||||||
|
fields = fields,
|
||||||
|
filters = [
|
||||||
|
[doctype, "docstatus", "=", 1],
|
||||||
|
[doctype, "is_return", "=", 1],
|
||||||
|
[child_doctype, reference_field, "=", row_name]
|
||||||
|
])
|
||||||
|
|
||||||
|
return data[0]
|
||||||
|
|
||||||
def make_return_doc(doctype, source_name, target_doc=None):
|
def make_return_doc(doctype, source_name, target_doc=None):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
company = frappe.db.get_value("Delivery Note", source_name, "company")
|
company = frappe.db.get_value("Delivery Note", source_name, "company")
|
||||||
default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return")
|
default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return")
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
doc = frappe.get_doc(target)
|
doc = frappe.get_doc(target)
|
||||||
doc.is_return = 1
|
doc.is_return = 1
|
||||||
@ -261,20 +288,25 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
doc.run_method("calculate_taxes_and_totals")
|
doc.run_method("calculate_taxes_and_totals")
|
||||||
|
|
||||||
def update_item(source_doc, target_doc, source_parent):
|
def update_item(source_doc, target_doc, source_parent):
|
||||||
target_doc.qty = -1* source_doc.qty
|
target_doc.qty = -1 * source_doc.qty
|
||||||
|
|
||||||
if doctype == "Purchase Receipt":
|
if doctype == "Purchase Receipt":
|
||||||
target_doc.received_qty = -1* source_doc.received_qty
|
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
|
||||||
target_doc.rejected_qty = -1* source_doc.rejected_qty
|
target_doc.received_qty = -1 * flt(source_doc.received_qty - (returned_qty_map.get('received_qty') or 0))
|
||||||
target_doc.qty = -1* source_doc.qty
|
target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get('rejected_qty') or 0))
|
||||||
target_doc.stock_qty = -1 * source_doc.stock_qty
|
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
|
||||||
|
|
||||||
|
target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
|
||||||
|
target_doc.received_stock_qty = -1 * flt(source_doc.received_stock_qty - (returned_qty_map.get('received_stock_qty') or 0))
|
||||||
|
|
||||||
target_doc.purchase_order = source_doc.purchase_order
|
target_doc.purchase_order = source_doc.purchase_order
|
||||||
target_doc.purchase_order_item = source_doc.purchase_order_item
|
target_doc.purchase_order_item = source_doc.purchase_order_item
|
||||||
target_doc.rejected_warehouse = source_doc.rejected_warehouse
|
target_doc.rejected_warehouse = source_doc.rejected_warehouse
|
||||||
target_doc.purchase_receipt_item = source_doc.name
|
target_doc.purchase_receipt_item = source_doc.name
|
||||||
|
|
||||||
elif doctype == "Purchase Invoice":
|
elif doctype == "Purchase Invoice":
|
||||||
target_doc.received_qty = -1* source_doc.received_qty
|
target_doc.received_qty = -1 * source_doc.received_qty
|
||||||
target_doc.rejected_qty = -1* source_doc.rejected_qty
|
target_doc.rejected_qty = -1 * source_doc.rejected_qty
|
||||||
target_doc.qty = -1* source_doc.qty
|
target_doc.qty = -1* source_doc.qty
|
||||||
target_doc.stock_qty = -1 * source_doc.stock_qty
|
target_doc.stock_qty = -1 * source_doc.stock_qty
|
||||||
target_doc.purchase_order = source_doc.purchase_order
|
target_doc.purchase_order = source_doc.purchase_order
|
||||||
@ -285,6 +317,10 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
target_doc.purchase_invoice_item = source_doc.name
|
target_doc.purchase_invoice_item = source_doc.name
|
||||||
|
|
||||||
elif doctype == "Delivery Note":
|
elif doctype == "Delivery Note":
|
||||||
|
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
|
||||||
|
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
|
||||||
|
target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
|
||||||
|
|
||||||
target_doc.against_sales_order = source_doc.against_sales_order
|
target_doc.against_sales_order = source_doc.against_sales_order
|
||||||
target_doc.against_sales_invoice = source_doc.against_sales_invoice
|
target_doc.against_sales_invoice = source_doc.against_sales_invoice
|
||||||
target_doc.so_detail = source_doc.so_detail
|
target_doc.so_detail = source_doc.so_detail
|
||||||
|
|||||||
@ -42,7 +42,7 @@ class SellingController(StockController):
|
|||||||
self.validate_max_discount()
|
self.validate_max_discount()
|
||||||
self.validate_selling_price()
|
self.validate_selling_price()
|
||||||
self.set_qty_as_per_stock_uom()
|
self.set_qty_as_per_stock_uom()
|
||||||
self.set_po_nos()
|
self.set_po_nos(for_validate=True)
|
||||||
self.set_gross_profit()
|
self.set_gross_profit()
|
||||||
set_default_income_account_for_item(self)
|
set_default_income_account_for_item(self)
|
||||||
self.set_customer_address()
|
self.set_customer_address()
|
||||||
@ -370,20 +370,28 @@ class SellingController(StockController):
|
|||||||
}))
|
}))
|
||||||
self.make_sl_entries(sl_entries)
|
self.make_sl_entries(sl_entries)
|
||||||
|
|
||||||
def set_po_nos(self):
|
def set_po_nos(self, for_validate=False):
|
||||||
if self.doctype == 'Sales Invoice' and hasattr(self, "items"):
|
if self.doctype == 'Sales Invoice' and hasattr(self, "items"):
|
||||||
|
if for_validate and self.po_no:
|
||||||
|
return
|
||||||
self.set_pos_for_sales_invoice()
|
self.set_pos_for_sales_invoice()
|
||||||
if self.doctype == 'Delivery Note' and hasattr(self, "items"):
|
if self.doctype == 'Delivery Note' and hasattr(self, "items"):
|
||||||
|
if for_validate and self.po_no:
|
||||||
|
return
|
||||||
self.set_pos_for_delivery_note()
|
self.set_pos_for_delivery_note()
|
||||||
|
|
||||||
def set_pos_for_sales_invoice(self):
|
def set_pos_for_sales_invoice(self):
|
||||||
po_nos = []
|
po_nos = []
|
||||||
|
if self.po_no:
|
||||||
|
po_nos.append(self.po_no)
|
||||||
self.get_po_nos('Sales Order', 'sales_order', po_nos)
|
self.get_po_nos('Sales Order', 'sales_order', po_nos)
|
||||||
self.get_po_nos('Delivery Note', 'delivery_note', po_nos)
|
self.get_po_nos('Delivery Note', 'delivery_note', po_nos)
|
||||||
self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
|
self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
|
||||||
|
|
||||||
def set_pos_for_delivery_note(self):
|
def set_pos_for_delivery_note(self):
|
||||||
po_nos = []
|
po_nos = []
|
||||||
|
if self.po_no:
|
||||||
|
po_nos.append(self.po_no)
|
||||||
self.get_po_nos('Sales Order', 'against_sales_order', po_nos)
|
self.get_po_nos('Sales Order', 'against_sales_order', po_nos)
|
||||||
self.get_po_nos('Sales Invoice', 'against_sales_invoice', po_nos)
|
self.get_po_nos('Sales Invoice', 'against_sales_invoice', po_nos)
|
||||||
self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
|
self.po_no = ', '.join(list(set(x.strip() for x in ','.join(po_nos).split(','))))
|
||||||
|
|||||||
@ -58,6 +58,7 @@ status_map = {
|
|||||||
"Delivery Note": [
|
"Delivery Note": [
|
||||||
["Draft", None],
|
["Draft", None],
|
||||||
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
||||||
|
["Return Issued", "eval:self.per_returned == 100 and self.docstatus == 1"],
|
||||||
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
["Closed", "eval:self.status=='Closed'"],
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
@ -65,6 +66,7 @@ status_map = {
|
|||||||
"Purchase Receipt": [
|
"Purchase Receipt": [
|
||||||
["Draft", None],
|
["Draft", None],
|
||||||
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
||||||
|
["Return Issued", "eval:self.per_returned == 100 and self.docstatus == 1"],
|
||||||
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
["Closed", "eval:self.status=='Closed'"],
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
@ -232,7 +234,7 @@ class StatusUpdater(Document):
|
|||||||
|
|
||||||
self._update_children(args, update_modified)
|
self._update_children(args, update_modified)
|
||||||
|
|
||||||
if "percent_join_field" in args:
|
if "percent_join_field" in args or "percent_join_field_parent" in args:
|
||||||
self._update_percent_field_in_targets(args, update_modified)
|
self._update_percent_field_in_targets(args, update_modified)
|
||||||
|
|
||||||
def _update_children(self, args, update_modified):
|
def _update_children(self, args, update_modified):
|
||||||
@ -272,6 +274,12 @@ class StatusUpdater(Document):
|
|||||||
|
|
||||||
def _update_percent_field_in_targets(self, args, update_modified=True):
|
def _update_percent_field_in_targets(self, args, update_modified=True):
|
||||||
"""Update percent field in parent transaction"""
|
"""Update percent field in parent transaction"""
|
||||||
|
if args.get('percent_join_field_parent'):
|
||||||
|
# if reference to target doc where % is to be updated, is
|
||||||
|
# in source doc's parent form, consider percent_join_field_parent
|
||||||
|
args['name'] = self.get(args['percent_join_field_parent'])
|
||||||
|
self._update_percent_field(args, update_modified)
|
||||||
|
else:
|
||||||
distinct_transactions = set([d.get(args['percent_join_field'])
|
distinct_transactions = set([d.get(args['percent_join_field'])
|
||||||
for d in self.get_all_children(args['source_dt'])])
|
for d in self.get_all_children(args['source_dt'])])
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,7 @@ class StockController(AccountsController):
|
|||||||
if sle_list:
|
if sle_list:
|
||||||
for sle in sle_list:
|
for sle in sle_list:
|
||||||
if warehouse_account.get(sle.warehouse):
|
if warehouse_account.get(sle.warehouse):
|
||||||
# from warehouse account/ target warehouse account
|
# from warehouse account
|
||||||
|
|
||||||
self.check_expense_account(item_row)
|
self.check_expense_account(item_row)
|
||||||
|
|
||||||
@ -92,9 +92,16 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
sle = self.update_stock_ledger_entries(sle)
|
sle = self.update_stock_ledger_entries(sle)
|
||||||
|
|
||||||
|
# expense account/ target_warehouse / source_warehouse
|
||||||
|
if item_row.get('target_warehouse'):
|
||||||
|
warehouse = item_row.get('target_warehouse')
|
||||||
|
expense_account = warehouse_account[warehouse]["account"]
|
||||||
|
else:
|
||||||
|
expense_account = item_row.expense_account
|
||||||
|
|
||||||
gl_list.append(self.get_gl_dict({
|
gl_list.append(self.get_gl_dict({
|
||||||
"account": warehouse_account[sle.warehouse]["account"],
|
"account": warehouse_account[sle.warehouse]["account"],
|
||||||
"against": item_row.expense_account,
|
"against": expense_account,
|
||||||
"cost_center": item_row.cost_center,
|
"cost_center": item_row.cost_center,
|
||||||
"project": item_row.project or self.get('project'),
|
"project": item_row.project or self.get('project'),
|
||||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||||
@ -102,9 +109,8 @@ class StockController(AccountsController):
|
|||||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
||||||
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
|
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
|
||||||
|
|
||||||
# expense account
|
|
||||||
gl_list.append(self.get_gl_dict({
|
gl_list.append(self.get_gl_dict({
|
||||||
"account": item_row.expense_account,
|
"account": expense_account,
|
||||||
"against": warehouse_account[sle.warehouse]["account"],
|
"against": warehouse_account[sle.warehouse]["account"],
|
||||||
"cost_center": item_row.cost_center,
|
"cost_center": item_row.cost_center,
|
||||||
"project": item_row.project or self.get('project'),
|
"project": item_row.project or self.get('project'),
|
||||||
@ -340,11 +346,15 @@ class StockController(AccountsController):
|
|||||||
validate_warehouse_company(w, self.company)
|
validate_warehouse_company(w, self.company)
|
||||||
|
|
||||||
def update_billing_percentage(self, update_modified=True):
|
def update_billing_percentage(self, update_modified=True):
|
||||||
|
target_ref_field = "amount"
|
||||||
|
if self.doctype == "Delivery Note":
|
||||||
|
target_ref_field = "amount - (returned_qty * rate)"
|
||||||
|
|
||||||
self._update_percent_field({
|
self._update_percent_field({
|
||||||
"target_dt": self.doctype + " Item",
|
"target_dt": self.doctype + " Item",
|
||||||
"target_parent_dt": self.doctype,
|
"target_parent_dt": self.doctype,
|
||||||
"target_parent_field": "per_billed",
|
"target_parent_field": "per_billed",
|
||||||
"target_ref_field": "amount",
|
"target_ref_field": target_ref_field,
|
||||||
"target_field": "billed_amt",
|
"target_field": "billed_amt",
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
}, update_modified)
|
}, update_modified)
|
||||||
|
|||||||
@ -519,6 +519,17 @@ class calculate_taxes_and_totals(object):
|
|||||||
if self.doc.docstatus == 0:
|
if self.doc.docstatus == 0:
|
||||||
self.calculate_outstanding_amount()
|
self.calculate_outstanding_amount()
|
||||||
|
|
||||||
|
def is_internal_invoice(self):
|
||||||
|
"""
|
||||||
|
Checks if its an internal transfer invoice
|
||||||
|
and decides if to calculate any out standing amount or not
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.doc.doctype in ('Sales Invoice', 'Purchase Invoice') and self.doc.is_internal_transfer():
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def calculate_outstanding_amount(self):
|
def calculate_outstanding_amount(self):
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# write_off_amount is only for POS Invoice
|
# write_off_amount is only for POS Invoice
|
||||||
@ -526,7 +537,8 @@ class calculate_taxes_and_totals(object):
|
|||||||
if self.doc.doctype == "Sales Invoice":
|
if self.doc.doctype == "Sales Invoice":
|
||||||
self.calculate_paid_amount()
|
self.calculate_paid_amount()
|
||||||
|
|
||||||
if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos'): return
|
if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos') or \
|
||||||
|
self.is_internal_invoice(): return
|
||||||
|
|
||||||
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
|
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
|
||||||
self._set_in_company_currency(self.doc, ['write_off_amount'])
|
self._set_in_company_currency(self.doc, ['write_off_amount'])
|
||||||
@ -641,7 +653,8 @@ class calculate_taxes_and_totals(object):
|
|||||||
if default_mode_of_payment:
|
if default_mode_of_payment:
|
||||||
self.doc.append('payments', {
|
self.doc.append('payments', {
|
||||||
'mode_of_payment': default_mode_of_payment.mode_of_payment,
|
'mode_of_payment': default_mode_of_payment.mode_of_payment,
|
||||||
'amount': total_amount_to_pay
|
'amount': total_amount_to_pay,
|
||||||
|
'default': 1
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
self.doc.is_pos = 0
|
self.doc.is_pos = 0
|
||||||
|
|||||||
@ -1,23 +1,31 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
cur_frm.add_fetch("contract_template", "contract_terms", "contract_terms");
|
|
||||||
cur_frm.add_fetch("contract_template", "requires_fulfilment", "requires_fulfilment");
|
|
||||||
|
|
||||||
// Add fulfilment terms from contract template into contract
|
|
||||||
frappe.ui.form.on("Contract", {
|
frappe.ui.form.on("Contract", {
|
||||||
contract_template: function (frm) {
|
contract_template: function (frm) {
|
||||||
// Populate the fulfilment terms table from a contract template, if any
|
|
||||||
if (frm.doc.contract_template) {
|
if (frm.doc.contract_template) {
|
||||||
frappe.model.with_doc("Contract Template", frm.doc.contract_template, function () {
|
frappe.call({
|
||||||
var tabletransfer = frappe.model.get_doc("Contract Template", frm.doc.contract_template);
|
method: 'erpnext.crm.doctype.contract_template.contract_template.get_contract_template',
|
||||||
|
args: {
|
||||||
|
template_name: frm.doc.contract_template,
|
||||||
|
doc: frm.doc
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r && r.message) {
|
||||||
|
let contract_template = r.message.contract_template;
|
||||||
|
frm.set_value("contract_terms", r.message.contract_terms);
|
||||||
|
frm.set_value("requires_fulfilment", contract_template.requires_fulfilment);
|
||||||
|
|
||||||
frm.doc.fulfilment_terms = [];
|
if (frm.doc.requires_fulfilment) {
|
||||||
$.each(tabletransfer.fulfilment_terms, function (index, row) {
|
// Populate the fulfilment terms table from a contract template, if any
|
||||||
var d = frm.add_child("fulfilment_terms");
|
r.message.contract_template.fulfilment_terms.forEach(element => {
|
||||||
d.requirement = row.requirement;
|
let d = frm.add_child("fulfilment_terms");
|
||||||
frm.refresh_field("fulfilment_terms");
|
d.requirement = element.requirement;
|
||||||
});
|
});
|
||||||
|
frm.refresh_field("fulfilment_terms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"creation": "2018-04-12 06:32:04.582486",
|
"creation": "2018-04-12 06:32:04.582486",
|
||||||
@ -247,7 +248,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-30 06:56:07.257932",
|
"modified": "2020-12-07 11:15:58.385521",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Contract",
|
"name": "Contract",
|
||||||
|
|||||||
@ -11,7 +11,9 @@
|
|||||||
"contract_terms",
|
"contract_terms",
|
||||||
"sb_fulfilment",
|
"sb_fulfilment",
|
||||||
"requires_fulfilment",
|
"requires_fulfilment",
|
||||||
"fulfilment_terms"
|
"fulfilment_terms",
|
||||||
|
"section_break_6",
|
||||||
|
"contract_template_help"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -41,10 +43,20 @@
|
|||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Fulfilment Terms and Conditions",
|
"label": "Fulfilment Terms and Conditions",
|
||||||
"options": "Contract Template Fulfilment Terms"
|
"options": "Contract Template Fulfilment Terms"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_6",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "contract_template_help",
|
||||||
|
"fieldtype": "HTML",
|
||||||
|
"label": "Contract Template Help",
|
||||||
|
"options": "<h4>Contract Template Example</h4>\n\n<pre>Contract for Customer {{ party_name }}\n\n-Valid From : {{ start_date }} \n-Valid To : {{ end_date }}\n</pre>\n\n<h4>How to get fieldnames</h4>\n\n<p>The field names you can use in your Contract Template are the fields in the Contract for which you are creating the template. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)</p>\n\n<h4>Templating</h4>\n\n<p>Templates are compiled using the Jinja Templating Language. To learn more about Jinja, <a class=\"strong\" href=\"http://jinja.pocoo.org/docs/dev/templates/\">read this documentation.</a></p>"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-11 17:49:44.879363",
|
"modified": "2020-12-07 10:44:22.587047",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Contract Template",
|
"name": "Contract Template",
|
||||||
|
|||||||
@ -5,6 +5,27 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils.jinja import validate_template
|
||||||
|
from six import string_types
|
||||||
|
import json
|
||||||
|
|
||||||
class ContractTemplate(Document):
|
class ContractTemplate(Document):
|
||||||
pass
|
def validate(self):
|
||||||
|
if self.contract_terms:
|
||||||
|
validate_template(self.contract_terms)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_contract_template(template_name, doc):
|
||||||
|
if isinstance(doc, string_types):
|
||||||
|
doc = json.loads(doc)
|
||||||
|
|
||||||
|
contract_template = frappe.get_doc("Contract Template", template_name)
|
||||||
|
contract_terms = None
|
||||||
|
|
||||||
|
if contract_template.contract_terms:
|
||||||
|
contract_terms = frappe.render_template(contract_template.contract_terms, doc)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'contract_template': contract_template,
|
||||||
|
'contract_terms': contract_terms
|
||||||
|
}
|
||||||
@ -134,7 +134,7 @@ def setup_employee():
|
|||||||
salary_component = frappe.get_doc('Salary Component', d.name)
|
salary_component = frappe.get_doc('Salary Component', d.name)
|
||||||
salary_component.append('accounts', dict(
|
salary_component.append('accounts', dict(
|
||||||
company=erpnext.get_default_company(),
|
company=erpnext.get_default_company(),
|
||||||
default_account=frappe.get_value('Account', dict(account_name=('like', 'Salary%')))
|
account=frappe.get_value('Account', dict(account_name=('like', 'Salary%')))
|
||||||
))
|
))
|
||||||
salary_component.save()
|
salary_component.save()
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
import taxjar
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from erpnext import get_default_company
|
from erpnext import get_default_company
|
||||||
from frappe import _
|
from frappe import _
|
||||||
@ -29,7 +31,6 @@ def get_client():
|
|||||||
|
|
||||||
|
|
||||||
def create_transaction(doc, method):
|
def create_transaction(doc, method):
|
||||||
import taxjar
|
|
||||||
"""Create an order transaction in TaxJar"""
|
"""Create an order transaction in TaxJar"""
|
||||||
|
|
||||||
if not TAXJAR_CREATE_TRANSACTIONS:
|
if not TAXJAR_CREATE_TRANSACTIONS:
|
||||||
|
|||||||
@ -61,3 +61,11 @@ def create_mode_of_payment(gateway, payment_type="General"):
|
|||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
mode_of_payment.insert(ignore_permissions=True)
|
mode_of_payment.insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
def get_tracking_url(carrier, tracking_number):
|
||||||
|
# Return the formatted Tracking URL.
|
||||||
|
tracking_url = ''
|
||||||
|
url_reference = frappe.get_value('Parcel Service', carrier, 'url_reference')
|
||||||
|
if url_reference:
|
||||||
|
tracking_url = frappe.render_template(url_reference, {'tracking_number': tracking_number})
|
||||||
|
return tracking_url
|
||||||
|
|||||||
@ -30,6 +30,11 @@
|
|||||||
"label": "Laboratory",
|
"label": "Laboratory",
|
||||||
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]"
|
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Inpatient",
|
||||||
|
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Order\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Entry\",\n\t\t\"label\": \"Inpatient Medication Entry\"\n\t}\n]"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Rehabilitation and Physiotherapy",
|
"label": "Rehabilitation and Physiotherapy",
|
||||||
@ -38,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Records and History",
|
"label": "Records and History",
|
||||||
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]"
|
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t}\n]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -64,7 +69,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Healthcare",
|
"label": "Healthcare",
|
||||||
"modified": "2020-11-23 23:00:48.764377",
|
"modified": "2020-11-26 22:09:09.164584",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Healthcare",
|
"name": "Healthcare",
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import nowdate, add_days
|
from frappe.utils import nowdate, add_days
|
||||||
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_appointment, create_healthcare_service_items
|
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_appointment, create_healthcare_service_items
|
||||||
|
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||||
|
|
||||||
test_dependencies = ["Company"]
|
test_dependencies = ["Company"]
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ class TestFeeValidity(unittest.TestCase):
|
|||||||
frappe.db.sql("""delete from `tabPatient Appointment`""")
|
frappe.db.sql("""delete from `tabPatient Appointment`""")
|
||||||
frappe.db.sql("""delete from `tabFee Validity`""")
|
frappe.db.sql("""delete from `tabFee Validity`""")
|
||||||
frappe.db.sql("""delete from `tabPatient`""")
|
frappe.db.sql("""delete from `tabPatient`""")
|
||||||
|
make_pos_profile()
|
||||||
|
|
||||||
def test_fee_validity(self):
|
def test_fee_validity(self):
|
||||||
item = create_healthcare_service_items()
|
item = create_healthcare_service_items()
|
||||||
|
|||||||
@ -29,6 +29,29 @@ frappe.ui.form.on('Inpatient Medication Entry', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (frm.doc.__islocal || frm.doc.docstatus !== 0 || !frm.doc.update_stock)
|
||||||
|
return;
|
||||||
|
|
||||||
|
frm.add_custom_button(__('Make Stock Entry'), function() {
|
||||||
|
frappe.call({
|
||||||
|
method: 'erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry.make_difference_stock_entry',
|
||||||
|
args: { docname: frm.doc.name },
|
||||||
|
freeze: true,
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
|
||||||
|
} else {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('No Drug Shortage'),
|
||||||
|
message: __('All the drugs are available with sufficient qty to process this Inpatient Medication Entry.'),
|
||||||
|
indicator: 'green'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
patient: function(frm) {
|
patient: function(frm) {
|
||||||
|
|||||||
@ -142,25 +142,32 @@ class InpatientMedicationEntry(Document):
|
|||||||
return orders, order_entry_map
|
return orders, order_entry_map
|
||||||
|
|
||||||
def check_stock_qty(self):
|
def check_stock_qty(self):
|
||||||
from erpnext.stock.stock_ledger import NegativeStockError
|
drug_shortage = get_drug_shortage_map(self.medication_orders, self.warehouse)
|
||||||
|
|
||||||
drug_availability = dict()
|
if drug_shortage:
|
||||||
for d in self.medication_orders:
|
message = _('Quantity not available for the following items in warehouse {0}. ').format(frappe.bold(self.warehouse))
|
||||||
if not drug_availability.get(d.drug_code):
|
message += _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.')
|
||||||
drug_availability[d.drug_code] = 0
|
|
||||||
drug_availability[d.drug_code] += flt(d.dosage)
|
|
||||||
|
|
||||||
for drug, dosage in drug_availability.items():
|
formatted_item_rows = ''
|
||||||
available_qty = get_latest_stock_qty(drug, self.warehouse)
|
|
||||||
|
|
||||||
# validate qty
|
for drug, shortage_qty in drug_shortage.items():
|
||||||
if flt(available_qty) < flt(dosage):
|
item_link = get_link_to_form('Item', drug)
|
||||||
frappe.throw(_('Quantity not available for {0} in warehouse {1}').format(
|
formatted_item_rows += """
|
||||||
frappe.bold(drug), frappe.bold(self.warehouse))
|
<td>{0}</td>
|
||||||
+ '<br><br>' + _('Available quantity is {0}, you need {1}').format(
|
<td>{1}</td>
|
||||||
frappe.bold(available_qty), frappe.bold(dosage))
|
</tr>""".format(item_link, frappe.bold(shortage_qty))
|
||||||
+ '<br><br>' + _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.'),
|
|
||||||
NegativeStockError, title=_('Insufficient Stock'))
|
message += """
|
||||||
|
<table class='table'>
|
||||||
|
<thead>
|
||||||
|
<th>{0}</th>
|
||||||
|
<th>{1}</th>
|
||||||
|
</thead>
|
||||||
|
{2}
|
||||||
|
</table>
|
||||||
|
""".format(_('Drug Code'), _('Shortage Qty'), formatted_item_rows)
|
||||||
|
|
||||||
|
frappe.throw(message, title=_('Insufficient Stock'), is_minimizable=True, wide=True)
|
||||||
|
|
||||||
def make_stock_entry(self):
|
def make_stock_entry(self):
|
||||||
stock_entry = frappe.new_doc('Stock Entry')
|
stock_entry = frappe.new_doc('Stock Entry')
|
||||||
@ -223,6 +230,7 @@ def get_pending_medication_orders(entry):
|
|||||||
|
|
||||||
for doc in data:
|
for doc in data:
|
||||||
inpatient_record = doc.inpatient_record
|
inpatient_record = doc.inpatient_record
|
||||||
|
if inpatient_record:
|
||||||
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
|
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
|
||||||
|
|
||||||
if entry.service_unit and doc.service_unit != entry.service_unit:
|
if entry.service_unit and doc.service_unit != entry.service_unit:
|
||||||
@ -277,3 +285,54 @@ def get_current_healthcare_service_unit(inpatient_record):
|
|||||||
if ip_record.inpatient_occupancies:
|
if ip_record.inpatient_occupancies:
|
||||||
return ip_record.inpatient_occupancies[-1].service_unit
|
return ip_record.inpatient_occupancies[-1].service_unit
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def get_drug_shortage_map(medication_orders, warehouse):
|
||||||
|
"""
|
||||||
|
Returns a dict like { drug_code: shortage_qty }
|
||||||
|
"""
|
||||||
|
drug_requirement = dict()
|
||||||
|
for d in medication_orders:
|
||||||
|
if not drug_requirement.get(d.drug_code):
|
||||||
|
drug_requirement[d.drug_code] = 0
|
||||||
|
drug_requirement[d.drug_code] += flt(d.dosage)
|
||||||
|
|
||||||
|
drug_shortage = dict()
|
||||||
|
for drug, required_qty in drug_requirement.items():
|
||||||
|
available_qty = get_latest_stock_qty(drug, warehouse)
|
||||||
|
if flt(required_qty) > flt(available_qty):
|
||||||
|
drug_shortage[drug] = flt(flt(required_qty) - flt(available_qty))
|
||||||
|
|
||||||
|
return drug_shortage
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_difference_stock_entry(docname):
|
||||||
|
doc = frappe.get_doc('Inpatient Medication Entry', docname)
|
||||||
|
drug_shortage = get_drug_shortage_map(doc.medication_orders, doc.warehouse)
|
||||||
|
|
||||||
|
if not drug_shortage:
|
||||||
|
return None
|
||||||
|
|
||||||
|
stock_entry = frappe.new_doc('Stock Entry')
|
||||||
|
stock_entry.purpose = 'Material Transfer'
|
||||||
|
stock_entry.set_stock_entry_type()
|
||||||
|
stock_entry.to_warehouse = doc.warehouse
|
||||||
|
stock_entry.company = doc.company
|
||||||
|
cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center')
|
||||||
|
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company)
|
||||||
|
|
||||||
|
for drug, shortage_qty in drug_shortage.items():
|
||||||
|
se_child = stock_entry.append('items')
|
||||||
|
se_child.item_code = drug
|
||||||
|
se_child.item_name = frappe.db.get_value('Item', drug, 'stock_uom')
|
||||||
|
se_child.uom = frappe.db.get_value('Item', drug, 'stock_uom')
|
||||||
|
se_child.stock_uom = se_child.uom
|
||||||
|
se_child.qty = flt(shortage_qty)
|
||||||
|
se_child.t_warehouse = doc.warehouse
|
||||||
|
# in stock uom
|
||||||
|
se_child.conversion_factor = 1
|
||||||
|
se_child.cost_center = cost_center
|
||||||
|
se_child.expense_account = expense_account
|
||||||
|
|
||||||
|
return stock_entry
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from frappe.utils import add_days, getdate, now_datetime
|
|||||||
from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
|
from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
|
||||||
from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
|
from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
|
||||||
from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme
|
from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme
|
||||||
|
from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_drug_shortage_map, make_difference_stock_entry
|
||||||
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
|
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
|
||||||
|
|
||||||
class TestInpatientMedicationEntry(unittest.TestCase):
|
class TestInpatientMedicationEntry(unittest.TestCase):
|
||||||
@ -82,6 +83,39 @@ class TestInpatientMedicationEntry(unittest.TestCase):
|
|||||||
self.assertEqual(stock_entry.items[0].patient, self.patient)
|
self.assertEqual(stock_entry.items[0].patient, self.patient)
|
||||||
self.assertEqual(stock_entry.items[0].inpatient_medication_entry_child, ipme.medication_orders[0].name)
|
self.assertEqual(stock_entry.items[0].inpatient_medication_entry_child, ipme.medication_orders[0].name)
|
||||||
|
|
||||||
|
def test_drug_shortage_stock_entry(self):
|
||||||
|
ipmo = create_ipmo(self.patient)
|
||||||
|
ipmo.submit()
|
||||||
|
ipmo.reload()
|
||||||
|
|
||||||
|
date = add_days(getdate(), -1)
|
||||||
|
filters = frappe._dict(
|
||||||
|
from_date=date,
|
||||||
|
to_date=date,
|
||||||
|
from_time='',
|
||||||
|
to_time='',
|
||||||
|
item_code='Dextromethorphan',
|
||||||
|
patient=self.patient
|
||||||
|
)
|
||||||
|
|
||||||
|
# check drug shortage
|
||||||
|
ipme = create_ipme(filters, update_stock=1)
|
||||||
|
ipme.warehouse = 'Finished Goods - _TC'
|
||||||
|
ipme.save()
|
||||||
|
drug_shortage = get_drug_shortage_map(ipme.medication_orders, ipme.warehouse)
|
||||||
|
self.assertEqual(drug_shortage.get('Dextromethorphan'), 3)
|
||||||
|
|
||||||
|
# check material transfer for drug shortage
|
||||||
|
make_stock_entry()
|
||||||
|
stock_entry = make_difference_stock_entry(ipme.name)
|
||||||
|
self.assertEqual(stock_entry.items[0].item_code, 'Dextromethorphan')
|
||||||
|
self.assertEqual(stock_entry.items[0].qty, 3)
|
||||||
|
stock_entry.from_warehouse = 'Stores - _TC'
|
||||||
|
stock_entry.submit()
|
||||||
|
|
||||||
|
ipme.reload()
|
||||||
|
ipme.submit()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# cleanup - Discharge
|
# cleanup - Discharge
|
||||||
schedule_discharge(frappe.as_json({'patient': self.patient}))
|
schedule_discharge(frappe.as_json({'patient': self.patient}))
|
||||||
@ -94,15 +128,12 @@ class TestInpatientMedicationEntry(unittest.TestCase):
|
|||||||
for entry in frappe.get_all('Inpatient Medication Entry'):
|
for entry in frappe.get_all('Inpatient Medication Entry'):
|
||||||
doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
|
doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
|
||||||
doc.cancel()
|
doc.cancel()
|
||||||
frappe.db.delete('Stock Entry', {'inpatient_medication_entry': doc.name})
|
|
||||||
doc.delete()
|
|
||||||
|
|
||||||
for entry in frappe.get_all('Inpatient Medication Order'):
|
for entry in frappe.get_all('Inpatient Medication Order'):
|
||||||
doc = frappe.get_doc('Inpatient Medication Order', entry.name)
|
doc = frappe.get_doc('Inpatient Medication Order', entry.name)
|
||||||
doc.cancel()
|
doc.cancel()
|
||||||
doc.delete()
|
|
||||||
|
|
||||||
def make_stock_entry():
|
def make_stock_entry(warehouse=None):
|
||||||
frappe.db.set_value('Company', '_Test Company', {
|
frappe.db.set_value('Company', '_Test Company', {
|
||||||
'stock_adjustment_account': 'Stock Adjustment - _TC',
|
'stock_adjustment_account': 'Stock Adjustment - _TC',
|
||||||
'default_inventory_account': 'Stock In Hand - _TC'
|
'default_inventory_account': 'Stock In Hand - _TC'
|
||||||
@ -110,7 +141,7 @@ def make_stock_entry():
|
|||||||
stock_entry = frappe.new_doc('Stock Entry')
|
stock_entry = frappe.new_doc('Stock Entry')
|
||||||
stock_entry.stock_entry_type = 'Material Receipt'
|
stock_entry.stock_entry_type = 'Material Receipt'
|
||||||
stock_entry.company = '_Test Company'
|
stock_entry.company = '_Test Company'
|
||||||
stock_entry.to_warehouse = 'Stores - _TC'
|
stock_entry.to_warehouse = warehouse or 'Stores - _TC'
|
||||||
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', '_Test Company')
|
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', '_Test Company')
|
||||||
se_child = stock_entry.append('items')
|
se_child = stock_entry.append('items')
|
||||||
se_child.item_code = 'Dextromethorphan'
|
se_child.item_code = 'Dextromethorphan'
|
||||||
|
|||||||
@ -18,6 +18,10 @@ def get_data():
|
|||||||
{
|
{
|
||||||
'label': _('Billing'),
|
'label': _('Billing'),
|
||||||
'items': ['Sales Invoice']
|
'items': ['Sales Invoice']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Orders'),
|
||||||
|
'items': ['Inpatient Medication Order']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,14 @@ import frappe
|
|||||||
from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status, make_encounter
|
from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status, make_encounter
|
||||||
from frappe.utils import nowdate, add_days
|
from frappe.utils import nowdate, add_days
|
||||||
from frappe.utils.make_random import get_random
|
from frappe.utils.make_random import get_random
|
||||||
|
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||||
|
|
||||||
class TestPatientAppointment(unittest.TestCase):
|
class TestPatientAppointment(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
frappe.db.sql("""delete from `tabPatient Appointment`""")
|
frappe.db.sql("""delete from `tabPatient Appointment`""")
|
||||||
frappe.db.sql("""delete from `tabFee Validity`""")
|
frappe.db.sql("""delete from `tabFee Validity`""")
|
||||||
frappe.db.sql("""delete from `tabPatient Encounter`""")
|
frappe.db.sql("""delete from `tabPatient Encounter`""")
|
||||||
|
make_pos_profile()
|
||||||
|
|
||||||
def test_status(self):
|
def test_status(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, medical_department, practitioner = create_healthcare_docs()
|
||||||
|
|||||||
@ -6,11 +6,13 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_encounter, create_healthcare_docs, create_appointment
|
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_encounter, create_healthcare_docs, create_appointment
|
||||||
|
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||||
|
|
||||||
class TestPatientMedicalRecord(unittest.TestCase):
|
class TestPatientMedicalRecord(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
|
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
|
||||||
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
|
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
|
||||||
|
make_pos_profile()
|
||||||
|
|
||||||
def test_medical_record(self):
|
def test_medical_record(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, medical_department, practitioner = create_healthcare_docs()
|
||||||
|
|||||||
@ -271,11 +271,11 @@ doc_events = {
|
|||||||
},
|
},
|
||||||
"Contact": {
|
"Contact": {
|
||||||
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
||||||
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information",
|
"after_insert": "erpnext.telephony.doctype.call_log.call_log.set_caller_information",
|
||||||
"validate": "erpnext.crm.utils.update_lead_phone_numbers"
|
"validate": "erpnext.crm.utils.update_lead_phone_numbers"
|
||||||
},
|
},
|
||||||
"Lead": {
|
"Lead": {
|
||||||
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information"
|
"after_insert": "erpnext.telephony.doctype.call_log.call_log.set_caller_information"
|
||||||
},
|
},
|
||||||
"Email Unsubscribe": {
|
"Email Unsubscribe": {
|
||||||
"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
|
"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
|
||||||
@ -441,42 +441,43 @@ global_search_doctypes = {
|
|||||||
{"doctype": "Sales Order", "index": 8},
|
{"doctype": "Sales Order", "index": 8},
|
||||||
{"doctype": "Quotation", "index": 9},
|
{"doctype": "Quotation", "index": 9},
|
||||||
{"doctype": "Work Order", "index": 10},
|
{"doctype": "Work Order", "index": 10},
|
||||||
{"doctype": "Purchase Receipt", "index": 11},
|
{"doctype": "Purchase Order", "index": 11},
|
||||||
{"doctype": "Purchase Invoice", "index": 12},
|
{"doctype": "Purchase Receipt", "index": 12},
|
||||||
{"doctype": "Delivery Note", "index": 13},
|
{"doctype": "Purchase Invoice", "index": 13},
|
||||||
{"doctype": "Stock Entry", "index": 14},
|
{"doctype": "Delivery Note", "index": 14},
|
||||||
{"doctype": "Material Request", "index": 15},
|
{"doctype": "Stock Entry", "index": 15},
|
||||||
{"doctype": "Delivery Trip", "index": 16},
|
{"doctype": "Material Request", "index": 16},
|
||||||
{"doctype": "Pick List", "index": 17},
|
{"doctype": "Delivery Trip", "index": 17},
|
||||||
{"doctype": "Salary Slip", "index": 18},
|
{"doctype": "Pick List", "index": 18},
|
||||||
{"doctype": "Leave Application", "index": 19},
|
{"doctype": "Salary Slip", "index": 19},
|
||||||
{"doctype": "Expense Claim", "index": 20},
|
{"doctype": "Leave Application", "index": 20},
|
||||||
{"doctype": "Payment Entry", "index": 21},
|
{"doctype": "Expense Claim", "index": 21},
|
||||||
{"doctype": "Lead", "index": 22},
|
{"doctype": "Payment Entry", "index": 22},
|
||||||
{"doctype": "Opportunity", "index": 23},
|
{"doctype": "Lead", "index": 23},
|
||||||
{"doctype": "Item Price", "index": 24},
|
{"doctype": "Opportunity", "index": 24},
|
||||||
{"doctype": "Purchase Taxes and Charges Template", "index": 25},
|
{"doctype": "Item Price", "index": 25},
|
||||||
{"doctype": "Sales Taxes and Charges", "index": 26},
|
{"doctype": "Purchase Taxes and Charges Template", "index": 26},
|
||||||
{"doctype": "Asset", "index": 27},
|
{"doctype": "Sales Taxes and Charges", "index": 27},
|
||||||
{"doctype": "Project", "index": 28},
|
{"doctype": "Asset", "index": 28},
|
||||||
{"doctype": "Task", "index": 29},
|
{"doctype": "Project", "index": 29},
|
||||||
{"doctype": "Timesheet", "index": 30},
|
{"doctype": "Task", "index": 30},
|
||||||
{"doctype": "Issue", "index": 31},
|
{"doctype": "Timesheet", "index": 31},
|
||||||
{"doctype": "Serial No", "index": 32},
|
{"doctype": "Issue", "index": 32},
|
||||||
{"doctype": "Batch", "index": 33},
|
{"doctype": "Serial No", "index": 33},
|
||||||
{"doctype": "Branch", "index": 34},
|
{"doctype": "Batch", "index": 34},
|
||||||
{"doctype": "Department", "index": 35},
|
{"doctype": "Branch", "index": 35},
|
||||||
{"doctype": "Employee Grade", "index": 36},
|
{"doctype": "Department", "index": 36},
|
||||||
{"doctype": "Designation", "index": 37},
|
{"doctype": "Employee Grade", "index": 37},
|
||||||
{"doctype": "Job Opening", "index": 38},
|
{"doctype": "Designation", "index": 38},
|
||||||
{"doctype": "Job Applicant", "index": 39},
|
{"doctype": "Job Opening", "index": 39},
|
||||||
{"doctype": "Job Offer", "index": 40},
|
{"doctype": "Job Applicant", "index": 40},
|
||||||
{"doctype": "Salary Structure Assignment", "index": 41},
|
{"doctype": "Job Offer", "index": 41},
|
||||||
{"doctype": "Appraisal", "index": 42},
|
{"doctype": "Salary Structure Assignment", "index": 42},
|
||||||
{"doctype": "Loan", "index": 43},
|
{"doctype": "Appraisal", "index": 43},
|
||||||
{"doctype": "Maintenance Schedule", "index": 44},
|
{"doctype": "Loan", "index": 44},
|
||||||
{"doctype": "Maintenance Visit", "index": 45},
|
{"doctype": "Maintenance Schedule", "index": 45},
|
||||||
{"doctype": "Warranty Claim", "index": 46},
|
{"doctype": "Maintenance Visit", "index": 46},
|
||||||
|
{"doctype": "Warranty Claim", "index": 47},
|
||||||
],
|
],
|
||||||
"Healthcare": [
|
"Healthcare": [
|
||||||
{'doctype': 'Patient', 'index': 1},
|
{'doctype': 'Patient', 'index': 1},
|
||||||
|
|||||||
@ -15,11 +15,16 @@ frappe.ui.form.on('Employee Advance', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("advance_account", function() {
|
frm.set_query("advance_account", function() {
|
||||||
|
if (!frm.doc.employee) {
|
||||||
|
frappe.msgprint(__("Please select employee first"));
|
||||||
|
}
|
||||||
|
var company_currency = erpnext.get_currency(frm.doc.company);
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"root_type": "Asset",
|
"root_type": "Asset",
|
||||||
"is_group": 0,
|
"is_group": 0,
|
||||||
"company": frm.doc.company
|
"company": frm.doc.company,
|
||||||
|
"account_currency": ["in", [frm.doc.currency, company_currency]],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -63,7 +68,7 @@ frappe.ui.form.on('Employee Advance', {
|
|||||||
}, __('Create'));
|
}, __('Create'));
|
||||||
}else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")){
|
}else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")){
|
||||||
frm.add_custom_button(__("Deduction from salary"), function() {
|
frm.add_custom_button(__("Deduction from salary"), function() {
|
||||||
frm.events.make_deduction_via_additional_salary(frm)
|
frm.events.make_deduction_via_additional_salary(frm);
|
||||||
}, __('Create'));
|
}, __('Create'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +132,9 @@ frappe.ui.form.on('Employee Advance', {
|
|||||||
'employee_advance_name': frm.doc.name,
|
'employee_advance_name': frm.doc.name,
|
||||||
'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount),
|
'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount),
|
||||||
'advance_account': frm.doc.advance_account,
|
'advance_account': frm.doc.advance_account,
|
||||||
'mode_of_payment': frm.doc.mode_of_payment
|
'mode_of_payment': frm.doc.mode_of_payment,
|
||||||
|
'currency': frm.doc.currency,
|
||||||
|
'exchange_rate': frm.doc.exchange_rate
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
const doclist = frappe.model.sync(r.message);
|
const doclist = frappe.model.sync(r.message);
|
||||||
@ -138,16 +145,72 @@ frappe.ui.form.on('Employee Advance', {
|
|||||||
|
|
||||||
employee: function (frm) {
|
employee: function (frm) {
|
||||||
if (frm.doc.employee) {
|
if (frm.doc.employee) {
|
||||||
return frappe.call({
|
frappe.run_serially([
|
||||||
|
() => frm.trigger('get_employee_currency'),
|
||||||
|
() => frm.trigger('get_pending_amount')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_pending_amount: function(frm) {
|
||||||
|
frappe.call({
|
||||||
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount",
|
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount",
|
||||||
args: {
|
args: {
|
||||||
"employee": frm.doc.employee,
|
"employee": frm.doc.employee,
|
||||||
"posting_date": frm.doc.posting_date
|
"posting_date": frm.doc.posting_date
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
frm.set_value("pending_amount",r.message);
|
frm.set_value("pending_amount", r.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
get_employee_currency: function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
|
||||||
|
args: {
|
||||||
|
employee: frm.doc.employee,
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
frm.set_value('currency', r.message);
|
||||||
|
frm.refresh_fields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
currency: function(frm) {
|
||||||
|
var from_currency = frm.doc.currency;
|
||||||
|
var company_currency;
|
||||||
|
if (!frm.doc.company) {
|
||||||
|
company_currency = erpnext.get_currency(frappe.defaults.get_default("Company"));
|
||||||
|
} else {
|
||||||
|
company_currency = erpnext.get_currency(frm.doc.company);
|
||||||
|
}
|
||||||
|
if (from_currency != company_currency) {
|
||||||
|
frm.events.set_exchange_rate(frm, from_currency, company_currency);
|
||||||
|
} else {
|
||||||
|
frm.set_value("exchange_rate", 1.0);
|
||||||
|
frm.set_df_property('exchange_rate', 'hidden', 1);
|
||||||
|
frm.set_df_property("exchange_rate", "description", "" );
|
||||||
|
}
|
||||||
|
frm.refresh_fields();
|
||||||
|
},
|
||||||
|
|
||||||
|
set_exchange_rate: function(frm, from_currency, company_currency) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.setup.utils.get_exchange_rate",
|
||||||
|
args: {
|
||||||
|
from_currency: from_currency,
|
||||||
|
to_currency: company_currency,
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
frm.set_value("exchange_rate", flt(r.message));
|
||||||
|
frm.set_df_property('exchange_rate', 'hidden', 0);
|
||||||
|
frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
|
||||||
|
+ " = [?] " + company_currency);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,6 +13,8 @@
|
|||||||
"department",
|
"department",
|
||||||
"column_break_4",
|
"column_break_4",
|
||||||
"posting_date",
|
"posting_date",
|
||||||
|
"currency",
|
||||||
|
"exchange_rate",
|
||||||
"repay_unclaimed_amount_from_salary",
|
"repay_unclaimed_amount_from_salary",
|
||||||
"section_break_8",
|
"section_break_8",
|
||||||
"purpose",
|
"purpose",
|
||||||
@ -91,7 +93,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Advance Amount",
|
"label": "Advance Amount",
|
||||||
"options": "Company:company:default_currency",
|
"options": "currency",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -99,7 +101,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Paid Amount",
|
"label": "Paid Amount",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Company:company:default_currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -107,7 +109,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Claimed Amount",
|
"label": "Claimed Amount",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Company:company:default_currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -161,7 +163,7 @@
|
|||||||
"fieldname": "return_amount",
|
"fieldname": "return_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Returned Amount",
|
"label": "Returned Amount",
|
||||||
"options": "Company:company:default_currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -175,13 +177,31 @@
|
|||||||
"fieldname": "pending_amount",
|
"fieldname": "pending_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Pending Amount",
|
"label": "Pending Amount",
|
||||||
"options": "Company:company:default_currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Company:company:default_currency",
|
||||||
|
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Currency",
|
||||||
|
"options": "Currency",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "currency",
|
||||||
|
"fieldname": "exchange_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Exchange Rate",
|
||||||
|
"precision": "9",
|
||||||
|
"print_hide": 1,
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-12 12:42:39.833818",
|
"modified": "2020-11-25 12:01:55.980721",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee Advance",
|
"name": "Employee Advance",
|
||||||
|
|||||||
@ -19,7 +19,6 @@ class EmployeeAdvance(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.validate_employee_advance_account()
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.ignore_linked_doctypes = ('GL Entry')
|
self.ignore_linked_doctypes = ('GL Entry')
|
||||||
@ -38,16 +37,9 @@ class EmployeeAdvance(Document):
|
|||||||
elif self.docstatus == 2:
|
elif self.docstatus == 2:
|
||||||
self.status = "Cancelled"
|
self.status = "Cancelled"
|
||||||
|
|
||||||
def validate_employee_advance_account(self):
|
|
||||||
company_currency = erpnext.get_company_currency(self.company)
|
|
||||||
if (self.advance_account and
|
|
||||||
company_currency != frappe.db.get_value('Account', self.advance_account, 'account_currency')):
|
|
||||||
frappe.throw(_("Advance account currency should be same as company currency {0}")
|
|
||||||
.format(company_currency))
|
|
||||||
|
|
||||||
def set_total_advance_paid(self):
|
def set_total_advance_paid(self):
|
||||||
paid_amount = frappe.db.sql("""
|
paid_amount = frappe.db.sql("""
|
||||||
select ifnull(sum(debit_in_account_currency), 0) as paid_amount
|
select ifnull(sum(debit), 0) as paid_amount
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where against_voucher_type = 'Employee Advance'
|
where against_voucher_type = 'Employee Advance'
|
||||||
and against_voucher = %s
|
and against_voucher = %s
|
||||||
@ -56,7 +48,7 @@ class EmployeeAdvance(Document):
|
|||||||
""", (self.name, self.employee), as_dict=1)[0].paid_amount
|
""", (self.name, self.employee), as_dict=1)[0].paid_amount
|
||||||
|
|
||||||
return_amount = frappe.db.sql("""
|
return_amount = frappe.db.sql("""
|
||||||
select name, ifnull(sum(credit_in_account_currency), 0) as return_amount
|
select ifnull(sum(credit), 0) as return_amount
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where against_voucher_type = 'Employee Advance'
|
where against_voucher_type = 'Employee Advance'
|
||||||
and voucher_type != 'Expense Claim'
|
and voucher_type != 'Expense Claim'
|
||||||
@ -65,6 +57,11 @@ class EmployeeAdvance(Document):
|
|||||||
and party = %s
|
and party = %s
|
||||||
""", (self.name, self.employee), as_dict=1)[0].return_amount
|
""", (self.name, self.employee), as_dict=1)[0].return_amount
|
||||||
|
|
||||||
|
if paid_amount != 0:
|
||||||
|
paid_amount = flt(paid_amount) / flt(self.exchange_rate)
|
||||||
|
if return_amount != 0:
|
||||||
|
return_amount = flt(return_amount) / flt(self.exchange_rate)
|
||||||
|
|
||||||
if flt(paid_amount) > self.advance_amount:
|
if flt(paid_amount) > self.advance_amount:
|
||||||
frappe.throw(_("Row {0}# Paid Amount cannot be greater than requested advance amount"),
|
frappe.throw(_("Row {0}# Paid Amount cannot be greater than requested advance amount"),
|
||||||
EmployeeAdvanceOverPayment)
|
EmployeeAdvanceOverPayment)
|
||||||
@ -107,16 +104,27 @@ def make_bank_entry(dt, dn):
|
|||||||
doc = frappe.get_doc(dt, dn)
|
doc = frappe.get_doc(dt, dn)
|
||||||
payment_account = get_default_bank_cash_account(doc.company, account_type="Cash",
|
payment_account = get_default_bank_cash_account(doc.company, account_type="Cash",
|
||||||
mode_of_payment=doc.mode_of_payment)
|
mode_of_payment=doc.mode_of_payment)
|
||||||
|
if not payment_account:
|
||||||
|
frappe.throw(_("Please set a Default Cash Account in Company defaults"))
|
||||||
|
|
||||||
|
advance_account_currency = frappe.db.get_value('Account', doc.advance_account, 'account_currency')
|
||||||
|
|
||||||
|
advance_amount, advance_exchange_rate = get_advance_amount_advance_exchange_rate(advance_account_currency,doc )
|
||||||
|
|
||||||
|
paying_amount, paying_exchange_rate = get_paying_amount_paying_exchange_rate(payment_account, doc)
|
||||||
|
|
||||||
je = frappe.new_doc("Journal Entry")
|
je = frappe.new_doc("Journal Entry")
|
||||||
je.posting_date = nowdate()
|
je.posting_date = nowdate()
|
||||||
je.voucher_type = 'Bank Entry'
|
je.voucher_type = 'Bank Entry'
|
||||||
je.company = doc.company
|
je.company = doc.company
|
||||||
je.remark = 'Payment against Employee Advance: ' + dn + '\n' + doc.purpose
|
je.remark = 'Payment against Employee Advance: ' + dn + '\n' + doc.purpose
|
||||||
|
je.multi_currency = 1 if advance_account_currency != payment_account.account_currency else 0
|
||||||
|
|
||||||
je.append("accounts", {
|
je.append("accounts", {
|
||||||
"account": doc.advance_account,
|
"account": doc.advance_account,
|
||||||
"debit_in_account_currency": flt(doc.advance_amount),
|
"account_currency": advance_account_currency,
|
||||||
|
"exchange_rate": flt(advance_exchange_rate),
|
||||||
|
"debit_in_account_currency": flt(advance_amount),
|
||||||
"reference_type": "Employee Advance",
|
"reference_type": "Employee Advance",
|
||||||
"reference_name": doc.name,
|
"reference_name": doc.name,
|
||||||
"party_type": "Employee",
|
"party_type": "Employee",
|
||||||
@ -128,19 +136,41 @@ def make_bank_entry(dt, dn):
|
|||||||
je.append("accounts", {
|
je.append("accounts", {
|
||||||
"account": payment_account.account,
|
"account": payment_account.account,
|
||||||
"cost_center": erpnext.get_default_cost_center(doc.company),
|
"cost_center": erpnext.get_default_cost_center(doc.company),
|
||||||
"credit_in_account_currency": flt(doc.advance_amount),
|
"credit_in_account_currency": flt(paying_amount),
|
||||||
"account_currency": payment_account.account_currency,
|
"account_currency": payment_account.account_currency,
|
||||||
"account_type": payment_account.account_type
|
"account_type": payment_account.account_type,
|
||||||
|
"exchange_rate": flt(paying_exchange_rate)
|
||||||
})
|
})
|
||||||
|
|
||||||
return je.as_dict()
|
return je.as_dict()
|
||||||
|
|
||||||
|
def get_advance_amount_advance_exchange_rate(advance_account_currency, doc):
|
||||||
|
if advance_account_currency != doc.currency:
|
||||||
|
advance_amount = flt(doc.advance_amount) * flt(doc.exchange_rate)
|
||||||
|
advance_exchange_rate = 1
|
||||||
|
else:
|
||||||
|
advance_amount = doc.advance_amount
|
||||||
|
advance_exchange_rate = doc.exchange_rate
|
||||||
|
|
||||||
|
return advance_amount, advance_exchange_rate
|
||||||
|
|
||||||
|
def get_paying_amount_paying_exchange_rate(payment_account, doc):
|
||||||
|
if payment_account.account_currency != doc.currency:
|
||||||
|
paying_amount = flt(doc.advance_amount) * flt(doc.exchange_rate)
|
||||||
|
paying_exchange_rate = 1
|
||||||
|
else:
|
||||||
|
paying_amount = doc.advance_amount
|
||||||
|
paying_exchange_rate = doc.exchange_rate
|
||||||
|
|
||||||
|
return paying_amount, paying_exchange_rate
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_return_through_additional_salary(doc):
|
def create_return_through_additional_salary(doc):
|
||||||
import json
|
import json
|
||||||
doc = frappe._dict(json.loads(doc))
|
doc = frappe._dict(json.loads(doc))
|
||||||
additional_salary = frappe.new_doc('Additional Salary')
|
additional_salary = frappe.new_doc('Additional Salary')
|
||||||
additional_salary.employee = doc.employee
|
additional_salary.employee = doc.employee
|
||||||
|
additional_salary.currency = doc.currency
|
||||||
additional_salary.amount = doc.paid_amount - doc.claimed_amount
|
additional_salary.amount = doc.paid_amount - doc.claimed_amount
|
||||||
additional_salary.company = doc.company
|
additional_salary.company = doc.company
|
||||||
additional_salary.ref_doctype = doc.doctype
|
additional_salary.ref_doctype = doc.doctype
|
||||||
@ -149,26 +179,28 @@ def create_return_through_additional_salary(doc):
|
|||||||
return additional_salary
|
return additional_salary
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_return_entry(employee, company, employee_advance_name, return_amount, advance_account, mode_of_payment=None):
|
def make_return_entry(employee, company, employee_advance_name, return_amount, advance_account, currency, exchange_rate, mode_of_payment=None):
|
||||||
return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
|
bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
|
||||||
|
if not bank_cash_account:
|
||||||
|
frappe.throw(_("Please set a Default Cash Account in Company defaults"))
|
||||||
|
|
||||||
mode_of_payment_type = ''
|
advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency')
|
||||||
if mode_of_payment:
|
|
||||||
mode_of_payment_type = frappe.get_cached_value('Mode of Payment', mode_of_payment, 'type')
|
|
||||||
if mode_of_payment_type not in ["Cash", "Bank"]:
|
|
||||||
# if mode of payment is General then it unset the type
|
|
||||||
mode_of_payment_type = None
|
|
||||||
|
|
||||||
je = frappe.new_doc('Journal Entry')
|
je = frappe.new_doc('Journal Entry')
|
||||||
je.posting_date = nowdate()
|
je.posting_date = nowdate()
|
||||||
# if mode of payment is Bank then voucher type is Bank Entry
|
je.voucher_type = get_voucher_type(mode_of_payment)
|
||||||
je.voucher_type = '{} Entry'.format(mode_of_payment_type) if mode_of_payment_type else 'Cash Entry'
|
|
||||||
je.company = company
|
je.company = company
|
||||||
je.remark = 'Return against Employee Advance: ' + employee_advance_name
|
je.remark = 'Return against Employee Advance: ' + employee_advance_name
|
||||||
|
je.multi_currency = 1 if advance_account_currency != bank_cash_account.account_currency else 0
|
||||||
|
|
||||||
|
advance_account_amount = flt(return_amount) if advance_account_currency==currency \
|
||||||
|
else flt(return_amount) * flt(exchange_rate)
|
||||||
|
|
||||||
je.append('accounts', {
|
je.append('accounts', {
|
||||||
'account': advance_account,
|
'account': advance_account,
|
||||||
'credit_in_account_currency': return_amount,
|
'credit_in_account_currency': advance_account_amount,
|
||||||
|
'account_currency': advance_account_currency,
|
||||||
|
'exchange_rate': flt(exchange_rate) if advance_account_currency == currency else 1,
|
||||||
'reference_type': 'Employee Advance',
|
'reference_type': 'Employee Advance',
|
||||||
'reference_name': employee_advance_name,
|
'reference_name': employee_advance_name,
|
||||||
'party_type': 'Employee',
|
'party_type': 'Employee',
|
||||||
@ -176,13 +208,25 @@ def make_return_entry(employee, company, employee_advance_name, return_amount,
|
|||||||
'is_advance': 'Yes'
|
'is_advance': 'Yes'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
bank_amount = flt(return_amount) if bank_cash_account.account_currency==currency \
|
||||||
|
else flt(return_amount) * flt(exchange_rate)
|
||||||
|
|
||||||
je.append("accounts", {
|
je.append("accounts", {
|
||||||
"account": return_account.account,
|
"account": bank_cash_account.account,
|
||||||
"debit_in_account_currency": return_amount,
|
"debit_in_account_currency": bank_amount,
|
||||||
"account_currency": return_account.account_currency,
|
"account_currency": bank_cash_account.account_currency,
|
||||||
"account_type": return_account.account_type
|
"account_type": bank_cash_account.account_type,
|
||||||
|
"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1
|
||||||
})
|
})
|
||||||
|
|
||||||
return je.as_dict()
|
return je.as_dict()
|
||||||
|
|
||||||
|
def get_voucher_type(mode_of_payment=None):
|
||||||
|
voucher_type = "Cash Entry"
|
||||||
|
|
||||||
|
if mode_of_payment:
|
||||||
|
mode_of_payment_type = frappe.get_cached_value('Mode of Payment', mode_of_payment, 'type')
|
||||||
|
if mode_of_payment_type == "Bank":
|
||||||
|
voucher_type = "Bank Entry"
|
||||||
|
|
||||||
|
return voucher_type
|
||||||
@ -3,15 +3,17 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import frappe
|
import frappe, erpnext
|
||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
from erpnext.hr.doctype.employee_advance.employee_advance import make_bank_entry
|
from erpnext.hr.doctype.employee_advance.employee_advance import make_bank_entry
|
||||||
from erpnext.hr.doctype.employee_advance.employee_advance import EmployeeAdvanceOverPayment
|
from erpnext.hr.doctype.employee_advance.employee_advance import EmployeeAdvanceOverPayment
|
||||||
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
class TestEmployeeAdvance(unittest.TestCase):
|
class TestEmployeeAdvance(unittest.TestCase):
|
||||||
def test_paid_amount_and_status(self):
|
def test_paid_amount_and_status(self):
|
||||||
advance = make_employee_advance()
|
employee_name = make_employee("_T@employe.advance")
|
||||||
|
advance = make_employee_advance(employee_name)
|
||||||
|
|
||||||
journal_entry = make_payment_entry(advance)
|
journal_entry = make_payment_entry(advance)
|
||||||
journal_entry.submit()
|
journal_entry.submit()
|
||||||
@ -33,11 +35,13 @@ def make_payment_entry(advance):
|
|||||||
|
|
||||||
return journal_entry
|
return journal_entry
|
||||||
|
|
||||||
def make_employee_advance():
|
def make_employee_advance(employee_name):
|
||||||
doc = frappe.new_doc("Employee Advance")
|
doc = frappe.new_doc("Employee Advance")
|
||||||
doc.employee = "_T-Employee-00001"
|
doc.employee = employee_name
|
||||||
doc.company = "_Test company"
|
doc.company = "_Test company"
|
||||||
doc.purpose = "For site visit"
|
doc.purpose = "For site visit"
|
||||||
|
doc.currency = erpnext.get_company_currency("_Test company")
|
||||||
|
doc.exchange_rate = 1
|
||||||
doc.advance_amount = 1000
|
doc.advance_amount = 1000
|
||||||
doc.posting_date = nowdate()
|
doc.posting_date = nowdate()
|
||||||
doc.advance_account = "_Test Employee Advance - _TC"
|
doc.advance_account = "_Test Employee Advance - _TC"
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import unittest
|
|||||||
from frappe.utils import random_string, nowdate
|
from frappe.utils import random_string, nowdate
|
||||||
from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
|
from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
|
||||||
from erpnext.accounts.doctype.account.test_account import create_account
|
from erpnext.accounts.doctype.account.test_account import create_account
|
||||||
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Expense Claim')
|
test_records = frappe.get_test_records('Expense Claim')
|
||||||
test_dependencies = ['Employee']
|
test_dependencies = ['Employee']
|
||||||
@ -126,6 +127,9 @@ def generate_taxes():
|
|||||||
|
|
||||||
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
||||||
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
||||||
|
if not employee:
|
||||||
|
employee = make_employee("test_employee@expense_claim.com", company=company)
|
||||||
|
|
||||||
currency, cost_center = frappe.db.get_value('Company', company, ['default_currency', 'cost_center'])
|
currency, cost_center = frappe.db.get_value('Company', company, ['default_currency', 'cost_center'])
|
||||||
expense_claim = {
|
expense_claim = {
|
||||||
"doctype": "Expense Claim",
|
"doctype": "Expense Claim",
|
||||||
|
|||||||
@ -71,9 +71,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"oldfieldname": "tax_amount",
|
"options": "currency"
|
||||||
"oldfieldtype": "Currency",
|
|
||||||
"options": "Company:company:default_currency"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
@ -81,9 +79,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Total",
|
"label": "Total",
|
||||||
"oldfieldname": "total",
|
"options": "currency",
|
||||||
"oldfieldtype": "Currency",
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -106,7 +102,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-11 19:01:26.611758",
|
"modified": "2020-09-23 20:27:36.027728",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Expense Taxes and Charges",
|
"name": "Expense Taxes and Charges",
|
||||||
|
|||||||
@ -376,24 +376,32 @@ class LeaveApplication(Document):
|
|||||||
if expiry_date:
|
if expiry_date:
|
||||||
self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit, lwp)
|
self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit, lwp)
|
||||||
else:
|
else:
|
||||||
|
raise_exception = True
|
||||||
|
if frappe.flags.in_patch:
|
||||||
|
raise_exception=False
|
||||||
|
|
||||||
args = dict(
|
args = dict(
|
||||||
leaves=self.total_leave_days * -1,
|
leaves=self.total_leave_days * -1,
|
||||||
from_date=self.from_date,
|
from_date=self.from_date,
|
||||||
to_date=self.to_date,
|
to_date=self.to_date,
|
||||||
is_lwp=lwp,
|
is_lwp=lwp,
|
||||||
holiday_list=get_holiday_list_for_employee(self.employee)
|
holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) or ''
|
||||||
)
|
)
|
||||||
create_leave_ledger_entry(self, args, submit)
|
create_leave_ledger_entry(self, args, submit)
|
||||||
|
|
||||||
def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit, lwp):
|
def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit, lwp):
|
||||||
''' splits leave application into two ledger entries to consider expiry of allocation '''
|
''' splits leave application into two ledger entries to consider expiry of allocation '''
|
||||||
|
|
||||||
|
raise_exception = True
|
||||||
|
if frappe.flags.in_patch:
|
||||||
|
raise_exception=False
|
||||||
|
|
||||||
args = dict(
|
args = dict(
|
||||||
from_date=self.from_date,
|
from_date=self.from_date,
|
||||||
to_date=expiry_date,
|
to_date=expiry_date,
|
||||||
leaves=(date_diff(expiry_date, self.from_date) + 1) * -1,
|
leaves=(date_diff(expiry_date, self.from_date) + 1) * -1,
|
||||||
is_lwp=lwp,
|
is_lwp=lwp,
|
||||||
holiday_list=get_holiday_list_for_employee(self.employee),
|
holiday_list=get_holiday_list_for_employee(self.employee, raise_exception=raise_exception) or ''
|
||||||
|
|
||||||
)
|
)
|
||||||
create_leave_ledger_entry(self, args, submit)
|
create_leave_ledger_entry(self, args, submit)
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,12 @@ frappe.ui.form.on('Leave Encashment', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
frm.trigger("get_leave_details_for_encashment");
|
if (frm.doc.employee) {
|
||||||
|
frappe.run_serially([
|
||||||
|
() => frm.trigger('get_employee_currency'),
|
||||||
|
() => frm.trigger('get_leave_details_for_encashment')
|
||||||
|
]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
leave_type: function(frm) {
|
leave_type: function(frm) {
|
||||||
frm.trigger("get_leave_details_for_encashment");
|
frm.trigger("get_leave_details_for_encashment");
|
||||||
@ -40,5 +45,20 @@ frappe.ui.form.on('Leave Encashment', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_employee_currency: function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
|
||||||
|
args: {
|
||||||
|
employee: frm.doc.employee,
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
frm.set_value('currency', r.message);
|
||||||
|
frm.refresh_fields();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
"employee",
|
"employee",
|
||||||
"employee_name",
|
"employee_name",
|
||||||
"department",
|
"department",
|
||||||
|
"company",
|
||||||
"column_break_4",
|
"column_break_4",
|
||||||
"leave_type",
|
"leave_type",
|
||||||
"leave_allocation",
|
"leave_allocation",
|
||||||
@ -19,9 +20,11 @@
|
|||||||
"encashable_days",
|
"encashable_days",
|
||||||
"amended_from",
|
"amended_from",
|
||||||
"payroll",
|
"payroll",
|
||||||
"encashment_amount",
|
|
||||||
"encashment_date",
|
"encashment_date",
|
||||||
"additional_salary"
|
"additional_salary",
|
||||||
|
"column_break_14",
|
||||||
|
"currency",
|
||||||
|
"encashment_amount"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -109,6 +112,7 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Encashment Amount",
|
"label": "Encashment Amount",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -124,11 +128,34 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Additional Salary",
|
"options": "Additional Salary",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Company:company:default_currency",
|
||||||
|
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Currency",
|
||||||
|
"options": "Currency",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_14",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.company",
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2019-12-16 11:51:57.732223",
|
"modified": "2020-11-25 11:56:06.777241",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Leave Encashment",
|
"name": "Leave Encashment",
|
||||||
|
|||||||
@ -16,10 +16,16 @@ class LeaveEncashment(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
set_employee_name(self)
|
set_employee_name(self)
|
||||||
self.get_leave_details_for_encashment()
|
self.get_leave_details_for_encashment()
|
||||||
|
self.validate_salary_structure()
|
||||||
|
|
||||||
if not self.encashment_date:
|
if not self.encashment_date:
|
||||||
self.encashment_date = getdate(nowdate())
|
self.encashment_date = getdate(nowdate())
|
||||||
|
|
||||||
|
def validate_salary_structure(self):
|
||||||
|
if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}):
|
||||||
|
frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee))
|
||||||
|
|
||||||
|
|
||||||
def before_submit(self):
|
def before_submit(self):
|
||||||
if self.encashment_amount <= 0:
|
if self.encashment_amount <= 0:
|
||||||
frappe.throw(_("You can only submit Leave Encashment for a valid encashment amount"))
|
frappe.throw(_("You can only submit Leave Encashment for a valid encashment amount"))
|
||||||
@ -30,6 +36,7 @@ class LeaveEncashment(Document):
|
|||||||
additional_salary = frappe.new_doc("Additional Salary")
|
additional_salary = frappe.new_doc("Additional Salary")
|
||||||
additional_salary.company = frappe.get_value("Employee", self.employee, "company")
|
additional_salary.company = frappe.get_value("Employee", self.employee, "company")
|
||||||
additional_salary.employee = self.employee
|
additional_salary.employee = self.employee
|
||||||
|
additional_salary.currency = self.currency
|
||||||
earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component")
|
earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component")
|
||||||
if not earning_component:
|
if not earning_component:
|
||||||
frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type))
|
frappe.throw(_("Please set Earning Component for Leave type: {0}.").format(self.leave_type))
|
||||||
|
|||||||
@ -48,6 +48,10 @@ class TestLeaveEncashment(unittest.TestCase):
|
|||||||
frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]).grant_leave_alloc_for_employee()
|
frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]).grant_leave_alloc_for_employee()
|
||||||
|
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for dt in ["Leave Period", "Leave Allocation", "Leave Ledger Entry", "Additional Salary", "Leave Encashment", "Salary Structure", "Leave Policy"]:
|
||||||
|
frappe.db.sql("delete from `tab%s`" % dt)
|
||||||
|
|
||||||
def test_leave_balance_value_and_amount(self):
|
def test_leave_balance_value_and_amount(self):
|
||||||
frappe.db.sql('''delete from `tabLeave Encashment`''')
|
frappe.db.sql('''delete from `tabLeave Encashment`''')
|
||||||
leave_encashment = frappe.get_doc(dict(
|
leave_encashment = frappe.get_doc(dict(
|
||||||
@ -55,7 +59,8 @@ class TestLeaveEncashment(unittest.TestCase):
|
|||||||
employee=self.employee,
|
employee=self.employee,
|
||||||
leave_type="_Test Leave Type Encashment",
|
leave_type="_Test Leave Type Encashment",
|
||||||
leave_period=self.leave_period.name,
|
leave_period=self.leave_period.name,
|
||||||
payroll_date=today()
|
payroll_date=today(),
|
||||||
|
currency="INR"
|
||||||
)).insert()
|
)).insert()
|
||||||
|
|
||||||
self.assertEqual(leave_encashment.leave_balance, 10)
|
self.assertEqual(leave_encashment.leave_balance, 10)
|
||||||
@ -75,7 +80,8 @@ class TestLeaveEncashment(unittest.TestCase):
|
|||||||
employee=self.employee,
|
employee=self.employee,
|
||||||
leave_type="_Test Leave Type Encashment",
|
leave_type="_Test Leave Type Encashment",
|
||||||
leave_period=self.leave_period.name,
|
leave_period=self.leave_period.name,
|
||||||
payroll_date=today()
|
payroll_date=today(),
|
||||||
|
currency="INR"
|
||||||
)).insert()
|
)).insert()
|
||||||
|
|
||||||
leave_encashment.submit()
|
leave_encashment.submit()
|
||||||
|
|||||||
@ -26,11 +26,11 @@
|
|||||||
"disbursed_amount",
|
"disbursed_amount",
|
||||||
"column_break_11",
|
"column_break_11",
|
||||||
"maximum_loan_amount",
|
"maximum_loan_amount",
|
||||||
"is_term_loan",
|
|
||||||
"repayment_method",
|
"repayment_method",
|
||||||
"repayment_periods",
|
"repayment_periods",
|
||||||
"monthly_repayment_amount",
|
"monthly_repayment_amount",
|
||||||
"repayment_start_date",
|
"repayment_start_date",
|
||||||
|
"is_term_loan",
|
||||||
"account_info",
|
"account_info",
|
||||||
"mode_of_payment",
|
"mode_of_payment",
|
||||||
"payment_account",
|
"payment_account",
|
||||||
|
|||||||
@ -13,6 +13,8 @@ from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calcul
|
|||||||
|
|
||||||
class Loan(AccountsController):
|
class Loan(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
if self.applicant_type == 'Employee' and self.repay_from_salary:
|
||||||
|
validate_employee_currency_with_company_currency(self.applicant, self.company)
|
||||||
self.set_loan_amount()
|
self.set_loan_amount()
|
||||||
self.validate_loan_amount()
|
self.validate_loan_amount()
|
||||||
self.set_missing_fields()
|
self.set_missing_fields()
|
||||||
@ -329,5 +331,14 @@ def create_loan_security_unpledge(unpledge_map, loan, company, applicant_type, a
|
|||||||
|
|
||||||
return unpledge_request
|
return unpledge_request
|
||||||
|
|
||||||
|
def validate_employee_currency_with_company_currency(applicant, company):
|
||||||
|
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_employee_currency
|
||||||
|
if not applicant:
|
||||||
|
frappe.throw(_("Please select Applicant"))
|
||||||
|
if not company:
|
||||||
|
frappe.throw(_("Please select Company"))
|
||||||
|
employee_currency = get_employee_currency(applicant)
|
||||||
|
company_currency = erpnext.get_company_currency(company)
|
||||||
|
if employee_currency != company_currency:
|
||||||
|
frappe.throw(_("Loan cannot be repayed from salary for Employee {0} because salary is processed in currency {1}")
|
||||||
|
.format(applicant, employee_currency))
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpled
|
|||||||
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
|
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
|
||||||
from erpnext.loan_management.doctype.loan_disbursement.loan_disbursement import get_disbursal_amount
|
from erpnext.loan_management.doctype.loan_disbursement.loan_disbursement import get_disbursal_amount
|
||||||
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
|
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
|
||||||
|
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
|
||||||
|
|
||||||
class TestLoan(unittest.TestCase):
|
class TestLoan(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -44,6 +45,7 @@ class TestLoan(unittest.TestCase):
|
|||||||
create_loan_security_price("Test Security 2", 250, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24)))
|
create_loan_security_price("Test Security 2", 250, "Nos", get_datetime() , get_datetime(add_to_date(nowdate(), hours=24)))
|
||||||
|
|
||||||
self.applicant1 = make_employee("robert_loan@loan.com")
|
self.applicant1 = make_employee("robert_loan@loan.com")
|
||||||
|
make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant1, currency='INR')
|
||||||
if not frappe.db.exists("Customer", "_Test Loan Customer"):
|
if not frappe.db.exists("Customer", "_Test Loan Customer"):
|
||||||
frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True)
|
frappe.get_doc(get_customer_dict('_Test Loan Customer')).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee
|
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee, make_salary_structure
|
||||||
from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan_accounts
|
from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan_accounts
|
||||||
|
|
||||||
class TestLoanApplication(unittest.TestCase):
|
class TestLoanApplication(unittest.TestCase):
|
||||||
@ -14,6 +14,7 @@ class TestLoanApplication(unittest.TestCase):
|
|||||||
create_loan_type("Home Loan", 500000, 9.2, 0, 1, 0, 'Cash', 'Payment Account - _TC', 'Loan Account - _TC',
|
create_loan_type("Home Loan", 500000, 9.2, 0, 1, 0, 'Cash', 'Payment Account - _TC', 'Loan Account - _TC',
|
||||||
'Interest Income Account - _TC', 'Penalty Income Account - _TC', 'Repay Over Number of Periods', 18)
|
'Interest Income Account - _TC', 'Penalty Income Account - _TC', 'Repay Over Number of Periods', 18)
|
||||||
self.applicant = make_employee("kate_loan@loan.com", "_Test Company")
|
self.applicant = make_employee("kate_loan@loan.com", "_Test Company")
|
||||||
|
make_salary_structure("Test Salary Structure Loan", "Monthly", employee=self.applicant, currency='INR')
|
||||||
self.create_loan_application()
|
self.create_loan_application()
|
||||||
|
|
||||||
def create_loan_application(self):
|
def create_loan_application(self):
|
||||||
@ -29,7 +30,6 @@ class TestLoanApplication(unittest.TestCase):
|
|||||||
})
|
})
|
||||||
loan_application.insert()
|
loan_application.insert()
|
||||||
|
|
||||||
|
|
||||||
def test_loan_totals(self):
|
def test_loan_totals(self):
|
||||||
loan_application = frappe.get_doc("Loan Application", {"applicant":self.applicant})
|
loan_application = frappe.get_doc("Loan Application", {"applicant":self.applicant})
|
||||||
|
|
||||||
|
|||||||
@ -169,8 +169,8 @@ class BOM(WebsiteGenerator):
|
|||||||
'qty' : args.get("qty") or args.get("stock_qty") or 1,
|
'qty' : args.get("qty") or args.get("stock_qty") or 1,
|
||||||
'stock_qty' : args.get("qty") or args.get("stock_qty") or 1,
|
'stock_qty' : args.get("qty") or args.get("stock_qty") or 1,
|
||||||
'base_rate' : flt(rate) * (flt(self.conversion_rate) or 1),
|
'base_rate' : flt(rate) * (flt(self.conversion_rate) or 1),
|
||||||
'include_item_in_manufacturing': cint(args['transfer_for_manufacture']) or 0,
|
'include_item_in_manufacturing': cint(args.get('transfer_for_manufacture')),
|
||||||
'sourced_by_supplier' : args['sourced_by_supplier'] or 0
|
'sourced_by_supplier' : args.get('sourced_by_supplier', 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret_item
|
return ret_item
|
||||||
|
|||||||
@ -124,7 +124,7 @@ class ProductionPlanReport(object):
|
|||||||
if self.filters.include_subassembly_raw_materials else "(bom_item.qty / bom.quantity)")
|
if self.filters.include_subassembly_raw_materials else "(bom_item.qty / bom.quantity)")
|
||||||
|
|
||||||
raw_materials = frappe.db.sql(""" SELECT bom_item.parent, bom_item.item_code,
|
raw_materials = frappe.db.sql(""" SELECT bom_item.parent, bom_item.item_code,
|
||||||
bom_item.item_name as raw_material_name, {0} as required_qty
|
bom_item.item_name as raw_material_name, {0} as required_qty_per_unit
|
||||||
FROM
|
FROM
|
||||||
`tabBOM` as bom, `tab{1}` as bom_item
|
`tabBOM` as bom, `tab{1}` as bom_item
|
||||||
WHERE
|
WHERE
|
||||||
@ -208,7 +208,7 @@ class ProductionPlanReport(object):
|
|||||||
warehouses = self.mrp_warehouses or []
|
warehouses = self.mrp_warehouses or []
|
||||||
for d in self.raw_materials_dict.get(key):
|
for d in self.raw_materials_dict.get(key):
|
||||||
if self.filters.based_on != "Work Order":
|
if self.filters.based_on != "Work Order":
|
||||||
d.required_qty = d.required_qty * data.qty_to_manufacture
|
d.required_qty = d.required_qty_per_unit * data.qty_to_manufacture
|
||||||
|
|
||||||
if not warehouses:
|
if not warehouses:
|
||||||
warehouses = [data.warehouse]
|
warehouses = [data.warehouse]
|
||||||
|
|||||||
@ -26,3 +26,4 @@ Quality Management
|
|||||||
Communication
|
Communication
|
||||||
Loan Management
|
Loan Management
|
||||||
Payroll
|
Payroll
|
||||||
|
Telephony
|
||||||
@ -691,6 +691,7 @@ erpnext.patches.v13_0.update_old_loans
|
|||||||
erpnext.patches.v12_0.set_serial_no_status #2020-05-21
|
erpnext.patches.v12_0.set_serial_no_status #2020-05-21
|
||||||
erpnext.patches.v12_0.update_price_list_currency_in_bom
|
erpnext.patches.v12_0.update_price_list_currency_in_bom
|
||||||
execute:frappe.reload_doctype('Dashboard')
|
execute:frappe.reload_doctype('Dashboard')
|
||||||
|
execute:frappe.reload_doc('desk', 'doctype', 'number_card_link')
|
||||||
execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts')
|
execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts')
|
||||||
erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo
|
erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo
|
||||||
erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25
|
erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25
|
||||||
@ -732,7 +733,11 @@ erpnext.patches.v13_0.set_youtube_video_id
|
|||||||
erpnext.patches.v13_0.print_uom_after_quantity_patch
|
erpnext.patches.v13_0.print_uom_after_quantity_patch
|
||||||
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
|
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
|
||||||
erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
|
erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
|
||||||
|
erpnext.patches.v13_0.updates_for_multi_currency_payroll
|
||||||
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
|
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
|
||||||
erpnext.patches.v13_0.update_custom_fields_for_shopify
|
erpnext.patches.v13_0.update_custom_fields_for_shopify
|
||||||
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
|
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
|
||||||
|
erpnext.patches.v13_0.updates_for_multi_currency_payroll
|
||||||
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
|
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
|
||||||
|
erpnext.patches.v13_0.add_po_to_global_search
|
||||||
|
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
||||||
|
|||||||
@ -8,8 +8,8 @@ from frappe.utils import getdate
|
|||||||
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import DuplicateAssignment
|
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import DuplicateAssignment
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doc('Payroll', 'doctype', 'salary_structure')
|
frappe.reload_doc('Payroll', 'doctype', 'Salary Structure')
|
||||||
frappe.reload_doc("Payroll", "doctype", "salary_structure_assignment")
|
frappe.reload_doc("Payroll", "doctype", "Salary Structure Assignment")
|
||||||
frappe.db.sql("""
|
frappe.db.sql("""
|
||||||
delete from `tabSalary Structure Assignment`
|
delete from `tabSalary Structure Assignment`
|
||||||
where salary_structure in (select name from `tabSalary Structure` where is_active='No' or docstatus!=1)
|
where salary_structure in (select name from `tabSalary Structure` where is_active='No' or docstatus!=1)
|
||||||
@ -33,6 +33,13 @@ def execute():
|
|||||||
AND employee in (select name from `tabEmployee` where ifNull(status, '') != 'Left')
|
AND employee in (select name from `tabEmployee` where ifNull(status, '') != 'Left')
|
||||||
""".format(cols), as_dict=1)
|
""".format(cols), as_dict=1)
|
||||||
|
|
||||||
|
all_companies = frappe.db.get_all("Company", fields=["name", "default_currency"])
|
||||||
|
for d in all_companies:
|
||||||
|
company = d.name
|
||||||
|
company_currency = d.default_currency
|
||||||
|
|
||||||
|
frappe.db.sql("""update `tabSalary Structure` set currency = %s where company=%s""", (company_currency, company))
|
||||||
|
|
||||||
for d in ss_details:
|
for d in ss_details:
|
||||||
try:
|
try:
|
||||||
joining_date, relieving_date = frappe.db.get_value("Employee", d.employee,
|
joining_date, relieving_date = frappe.db.get_value("Employee", d.employee,
|
||||||
@ -42,6 +49,7 @@ def execute():
|
|||||||
from_date = joining_date
|
from_date = joining_date
|
||||||
elif relieving_date and getdate(from_date) > relieving_date:
|
elif relieving_date and getdate(from_date) > relieving_date:
|
||||||
continue
|
continue
|
||||||
|
company_currency = frappe.db.get_value('Company', d.company, 'default_currency')
|
||||||
|
|
||||||
s = frappe.new_doc("Salary Structure Assignment")
|
s = frappe.new_doc("Salary Structure Assignment")
|
||||||
s.employee = d.employee
|
s.employee = d.employee
|
||||||
@ -52,6 +60,7 @@ def execute():
|
|||||||
s.base = d.get("base")
|
s.base = d.get("base")
|
||||||
s.variable = d.get("variable")
|
s.variable = d.get("variable")
|
||||||
s.company = d.company
|
s.company = d.company
|
||||||
|
s.currency = company_currency
|
||||||
|
|
||||||
# to migrate the data of the old employees
|
# to migrate the data of the old employees
|
||||||
s.flags.old_employee = True
|
s.flags.old_employee = True
|
||||||
|
|||||||
17
erpnext/patches/v13_0/add_po_to_global_search.py
Normal file
17
erpnext/patches/v13_0/add_po_to_global_search.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
global_search_settings = frappe.get_single("Global Search Settings")
|
||||||
|
|
||||||
|
if "Purchase Order" in (
|
||||||
|
dt.document_type for dt in global_search_settings.allowed_in_global_search
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
global_search_settings.append(
|
||||||
|
"allowed_in_global_search", {"document_type": "Purchase Order"}
|
||||||
|
)
|
||||||
|
|
||||||
|
global_search_settings.save(ignore_permissions=True)
|
||||||
@ -52,6 +52,8 @@ def create_assignment(employee, leave_policy, leave_period=None, allocation_exis
|
|||||||
if leave_period:
|
if leave_period:
|
||||||
filters["leave_period"] = leave_period
|
filters["leave_period"] = leave_period
|
||||||
|
|
||||||
|
frappe.reload_doc('hr', 'doctype', 'leave_policy_assignment')
|
||||||
|
|
||||||
if not frappe.db.exists("Leave Policy Assignment" , filters):
|
if not frappe.db.exists("Leave Policy Assignment" , filters):
|
||||||
lpa = frappe.new_doc("Leave Policy Assignment")
|
lpa = frappe.new_doc("Leave Policy Assignment")
|
||||||
lpa.employee = employee
|
lpa.employee = employee
|
||||||
|
|||||||
@ -29,7 +29,7 @@ def execute():
|
|||||||
'response_by_variance': response_by_variance,
|
'response_by_variance': response_by_variance,
|
||||||
'resolution_by_variance': resolution_by_variance,
|
'resolution_by_variance': resolution_by_variance,
|
||||||
'first_response_time': mins_to_first_response
|
'first_response_time': mins_to_first_response
|
||||||
})
|
}, update_modified=False)
|
||||||
# commit after every 100 updates
|
# commit after every 100 updates
|
||||||
count += 1
|
count += 1
|
||||||
if count%100 == 0:
|
if count%100 == 0:
|
||||||
@ -44,7 +44,7 @@ def execute():
|
|||||||
count = 0
|
count = 0
|
||||||
for entry in opportunities:
|
for entry in opportunities:
|
||||||
mins_to_first_response = convert_to_seconds(entry.mins_to_first_response, 'Minutes')
|
mins_to_first_response = convert_to_seconds(entry.mins_to_first_response, 'Minutes')
|
||||||
frappe.db.set_value('Opportunity', entry.name, 'first_response_time', mins_to_first_response)
|
frappe.db.set_value('Opportunity', entry.name, 'first_response_time', mins_to_first_response, update_modified=False)
|
||||||
# commit after every 100 updates
|
# commit after every 100 updates
|
||||||
count += 1
|
count += 1
|
||||||
if count%100 == 0:
|
if count%100 == 0:
|
||||||
|
|||||||
27
erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py
Normal file
27
erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Copyright (c) 2019, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'delivery_note')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'delivery_note_item')
|
||||||
|
|
||||||
|
def update_from_return_docs(doctype):
|
||||||
|
for return_doc in frappe.get_all(doctype, filters={'is_return' : 1, 'docstatus' : 1}):
|
||||||
|
# Update original receipt/delivery document from return
|
||||||
|
return_doc = frappe.get_cached_doc(doctype, return_doc.name)
|
||||||
|
return_doc.update_prevdoc_status()
|
||||||
|
return_against = frappe.get_doc(doctype, return_doc.return_against)
|
||||||
|
return_against.update_billing_status()
|
||||||
|
|
||||||
|
# Set received qty in stock uom in PR, as returned qty is checked against it
|
||||||
|
frappe.db.sql(""" update `tabPurchase Receipt Item`
|
||||||
|
set received_stock_qty = received_qty * conversion_factor
|
||||||
|
where docstatus = 1 """)
|
||||||
|
|
||||||
|
for doctype in ('Purchase Receipt', 'Delivery Note'):
|
||||||
|
update_from_return_docs(doctype)
|
||||||
136
erpnext/patches/v13_0/updates_for_multi_currency_payroll.py
Normal file
136
erpnext/patches/v13_0/updates_for_multi_currency_payroll.py
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# Copyright (c) 2019, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.model.utils.rename_field import rename_field
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
|
||||||
|
frappe.reload_doc('Accounts', 'doctype', 'Salary Component Account')
|
||||||
|
if frappe.db.has_column('Salary Component Account', 'default_account'):
|
||||||
|
rename_field("Salary Component Account", "default_account", "account")
|
||||||
|
|
||||||
|
doctype_list = [
|
||||||
|
{
|
||||||
|
'module':'HR',
|
||||||
|
'doctype':'Employee Advance'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'HR',
|
||||||
|
'doctype':'Leave Encashment'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Additional Salary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Employee Benefit Application'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Employee Benefit Claim'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Employee Incentive'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Employee Tax Exemption Declaration'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Employee Tax Exemption Proof Submission'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Income Tax Slab'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Payroll Entry'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Retention Bonus'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Salary Structure'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Salary Structure Assignment'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'module':'Payroll',
|
||||||
|
'doctype':'Salary Slip'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
for item in doctype_list:
|
||||||
|
frappe.reload_doc(item['module'], 'doctype', item['doctype'])
|
||||||
|
|
||||||
|
# update company in employee advance based on employee company
|
||||||
|
for dt in ['Employee Incentive', 'Leave Encashment', 'Employee Benefit Application', 'Employee Benefit Claim']:
|
||||||
|
frappe.db.sql("""
|
||||||
|
update `tab{doctype}`
|
||||||
|
set company = (select company from tabEmployee where name=`tab{doctype}`.employee)
|
||||||
|
""".format(doctype=dt))
|
||||||
|
|
||||||
|
# update exchange rate for employee advance
|
||||||
|
frappe.db.sql("update `tabEmployee Advance` set exchange_rate=1")
|
||||||
|
|
||||||
|
# get all companies and it's currency
|
||||||
|
all_companies = frappe.db.get_all("Company", fields=["name", "default_currency", "default_payroll_payable_account"])
|
||||||
|
for d in all_companies:
|
||||||
|
company = d.name
|
||||||
|
company_currency = d.default_currency
|
||||||
|
default_payroll_payable_account = d.default_payroll_payable_account
|
||||||
|
|
||||||
|
if not default_payroll_payable_account:
|
||||||
|
default_payroll_payable_account = frappe.db.get_value("Account",
|
||||||
|
{"account_name": _("Payroll Payable"), "company": company, "account_currency": company_currency, "is_group": 0})
|
||||||
|
|
||||||
|
# update currency in following doctypes based on company currency
|
||||||
|
doctypes_for_currency = ['Employee Advance', 'Leave Encashment', 'Employee Benefit Application',
|
||||||
|
'Employee Benefit Claim', 'Employee Incentive', 'Additional Salary',
|
||||||
|
'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission',
|
||||||
|
'Income Tax Slab', 'Retention Bonus', 'Salary Structure']
|
||||||
|
|
||||||
|
for dt in doctypes_for_currency:
|
||||||
|
frappe.db.sql("""update `tab{doctype}` set currency = %s where company=%s"""
|
||||||
|
.format(doctype=dt), (company_currency, company))
|
||||||
|
|
||||||
|
# update fields in payroll entry
|
||||||
|
frappe.db.sql("""
|
||||||
|
update `tabPayroll Entry`
|
||||||
|
set currency = %s,
|
||||||
|
exchange_rate = 1,
|
||||||
|
payroll_payable_account=%s
|
||||||
|
where company=%s
|
||||||
|
""", (company_currency, default_payroll_payable_account, company))
|
||||||
|
|
||||||
|
# update fields in Salary Structure Assignment
|
||||||
|
frappe.db.sql("""
|
||||||
|
update `tabSalary Structure Assignment`
|
||||||
|
set currency = %s,
|
||||||
|
payroll_payable_account=%s
|
||||||
|
where company=%s
|
||||||
|
""", (company_currency, default_payroll_payable_account, company))
|
||||||
|
|
||||||
|
# update fields in Salary Slip
|
||||||
|
frappe.db.sql("""
|
||||||
|
update `tabSalary Slip`
|
||||||
|
set currency = %s,
|
||||||
|
exchange_rate = 1,
|
||||||
|
base_hour_rate = hour_rate,
|
||||||
|
base_gross_pay = gross_pay,
|
||||||
|
base_total_deduction = total_deduction,
|
||||||
|
base_net_pay = net_pay,
|
||||||
|
base_rounded_total = rounded_total,
|
||||||
|
base_total_in_words = total_in_words
|
||||||
|
where company=%s
|
||||||
|
""", (company_currency, company))
|
||||||
@ -7,6 +7,10 @@ import frappe
|
|||||||
def execute():
|
def execute():
|
||||||
parent_list = []
|
parent_list = []
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item')
|
||||||
|
|
||||||
for data in frappe.db.sql("""
|
for data in frappe.db.sql("""
|
||||||
select
|
select
|
||||||
`tabPurchase Receipt Item`.purchase_order, `tabPurchase Receipt Item`.name,
|
`tabPurchase Receipt Item`.purchase_order, `tabPurchase Receipt Item`.name,
|
||||||
|
|||||||
@ -12,5 +12,57 @@ frappe.ui.form.on('Additional Salary', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!frm.doc.currency) return;
|
||||||
|
frm.set_query("salary_component", function() {
|
||||||
|
return {
|
||||||
|
query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
|
||||||
|
filters: {currency: frm.doc.currency, company: frm.doc.company}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
employee: function(frm) {
|
||||||
|
if (frm.doc.employee) {
|
||||||
|
frappe.run_serially([
|
||||||
|
() => frm.trigger('get_employee_currency'),
|
||||||
|
() => frm.trigger('set_company')
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
frm.set_value("company", null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
set_company: function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: "frappe.client.get_value",
|
||||||
|
args: {
|
||||||
|
doctype: "Employee",
|
||||||
|
fieldname: "company",
|
||||||
|
filters: {
|
||||||
|
name: frm.doc.employee
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback: function(data) {
|
||||||
|
if (data.message) {
|
||||||
|
frm.set_value("company", data.message.company);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
get_employee_currency: function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
|
||||||
|
args: {
|
||||||
|
employee: frm.doc.employee,
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
frm.set_value('currency', r.message);
|
||||||
|
frm.refresh_fields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,20 +11,21 @@
|
|||||||
"employee",
|
"employee",
|
||||||
"employee_name",
|
"employee_name",
|
||||||
"salary_component",
|
"salary_component",
|
||||||
"overwrite_salary_structure_amount",
|
"type",
|
||||||
"deduct_full_tax_on_selected_payroll_date",
|
"amount",
|
||||||
"ref_doctype",
|
"ref_doctype",
|
||||||
"ref_docname",
|
"ref_docname",
|
||||||
|
"amended_from",
|
||||||
"column_break_5",
|
"column_break_5",
|
||||||
"company",
|
"company",
|
||||||
"is_recurring",
|
"department",
|
||||||
|
"currency",
|
||||||
"from_date",
|
"from_date",
|
||||||
"to_date",
|
"to_date",
|
||||||
"payroll_date",
|
"payroll_date",
|
||||||
"type",
|
"is_recurring",
|
||||||
"department",
|
"overwrite_salary_structure_amount",
|
||||||
"amount",
|
"deduct_full_tax_on_selected_payroll_date"
|
||||||
"amended_from"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -59,6 +60,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
|
"options": "currency",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -159,11 +161,22 @@
|
|||||||
"label": "Reference Document",
|
"label": "Reference Document",
|
||||||
"options": "ref_doctype",
|
"options": "ref_doctype",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Company:company:default_currency",
|
||||||
|
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Currency",
|
||||||
|
"options": "Currency",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-22 21:10:50.374063",
|
"modified": "2020-10-20 17:51:13.419716",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Additional Salary",
|
"name": "Additional Salary",
|
||||||
|
|||||||
@ -22,10 +22,15 @@ class AdditionalSalary(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
|
self.validate_salary_structure()
|
||||||
self.validate_recurring_additional_salary_overlap()
|
self.validate_recurring_additional_salary_overlap()
|
||||||
if self.amount < 0:
|
if self.amount < 0:
|
||||||
frappe.throw(_("Amount should not be less than zero."))
|
frappe.throw(_("Amount should not be less than zero."))
|
||||||
|
|
||||||
|
def validate_salary_structure(self):
|
||||||
|
if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}):
|
||||||
|
frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee))
|
||||||
|
|
||||||
def validate_recurring_additional_salary_overlap(self):
|
def validate_recurring_additional_salary_overlap(self):
|
||||||
if self.is_recurring:
|
if self.is_recurring:
|
||||||
additional_salaries = frappe.db.sql("""
|
additional_salaries = frappe.db.sql("""
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from frappe.utils import nowdate, add_days
|
|||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
|
from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
|
||||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test
|
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test
|
||||||
|
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
|
||||||
|
|
||||||
|
|
||||||
class TestAdditionalSalary(unittest.TestCase):
|
class TestAdditionalSalary(unittest.TestCase):
|
||||||
@ -15,12 +16,19 @@ class TestAdditionalSalary(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
setup_test()
|
setup_test()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for dt in ["Salary Slip", "Additional Salary", "Salary Structure Assignment", "Salary Structure"]:
|
||||||
|
frappe.db.sql("delete from `tab%s`" % dt)
|
||||||
|
|
||||||
def test_recurring_additional_salary(self):
|
def test_recurring_additional_salary(self):
|
||||||
|
amount = 0
|
||||||
|
salary_component = None
|
||||||
emp_id = make_employee("test_additional@salary.com")
|
emp_id = make_employee("test_additional@salary.com")
|
||||||
frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800))
|
frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800))
|
||||||
|
salary_structure = make_salary_structure("Test Salary Structure Additional Salary", "Monthly", employee=emp_id)
|
||||||
add_sal = get_additional_salary(emp_id)
|
add_sal = get_additional_salary(emp_id)
|
||||||
|
|
||||||
ss = make_employee_salary_slip("test_additional@salary.com", "Monthly")
|
ss = make_employee_salary_slip("test_additional@salary.com", "Monthly", salary_structure=salary_structure.name)
|
||||||
for earning in ss.earnings:
|
for earning in ss.earnings:
|
||||||
if earning.salary_component == "Recurring Salary Component":
|
if earning.salary_component == "Recurring Salary Component":
|
||||||
amount = earning.amount
|
amount = earning.amount
|
||||||
@ -29,8 +37,6 @@ class TestAdditionalSalary(unittest.TestCase):
|
|||||||
self.assertEqual(amount, add_sal.amount)
|
self.assertEqual(amount, add_sal.amount)
|
||||||
self.assertEqual(salary_component, add_sal.salary_component)
|
self.assertEqual(salary_component, add_sal.salary_component)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_additional_salary(emp_id):
|
def get_additional_salary(emp_id):
|
||||||
create_salary_component("Recurring Salary Component")
|
create_salary_component("Recurring Salary Component")
|
||||||
add_sal = frappe.new_doc("Additional Salary")
|
add_sal = frappe.new_doc("Additional Salary")
|
||||||
@ -40,6 +46,7 @@ def get_additional_salary(emp_id):
|
|||||||
add_sal.from_date = add_days(nowdate(), -50)
|
add_sal.from_date = add_days(nowdate(), -50)
|
||||||
add_sal.to_date = add_days(nowdate(), 180)
|
add_sal.to_date = add_days(nowdate(), 180)
|
||||||
add_sal.amount = 5000
|
add_sal.amount = 5000
|
||||||
|
add_sal.currency = erpnext.get_default_currency()
|
||||||
add_sal.save()
|
add_sal.save()
|
||||||
add_sal.submit()
|
add_sal.submit()
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,12 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Employee Benefit Application', {
|
frappe.ui.form.on('Employee Benefit Application', {
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
frm.trigger('set_earning_component');
|
if (frm.doc.employee) {
|
||||||
|
frappe.run_serially([
|
||||||
|
() => frm.trigger('get_employee_currency'),
|
||||||
|
() => frm.trigger('set_earning_component')
|
||||||
|
]);
|
||||||
|
}
|
||||||
var method, args;
|
var method, args;
|
||||||
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
||||||
method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
|
method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
|
||||||
@ -38,9 +43,26 @@ frappe.ui.form.on('Employee Benefit Application', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get_employee_currency: function(frm) {
|
||||||
|
if (frm.doc.employee) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
|
||||||
|
args: {
|
||||||
|
employee: frm.doc.employee,
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
frm.set_value('currency', r.message);
|
||||||
|
frm.refresh_fields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
payroll_period: function(frm) {
|
payroll_period: function(frm) {
|
||||||
var method, args;
|
var method, args;
|
||||||
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
if (frm.doc.employee && frm.doc.date && frm.doc.payroll_period) {
|
||||||
method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
|
method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
|
||||||
args = {
|
args = {
|
||||||
employee: frm.doc.employee,
|
employee: frm.doc.employee,
|
||||||
@ -60,11 +82,14 @@ var get_max_benefits=function(frm, method, args) {
|
|||||||
method: method,
|
method: method,
|
||||||
args: args,
|
args: args,
|
||||||
callback: function (data) {
|
callback: function (data) {
|
||||||
if(!data.exc){
|
if (!data.exc) {
|
||||||
if(data.message){
|
if (data.message) {
|
||||||
frm.set_value("max_benefits", data.message);
|
frm.set_value("max_benefits", data.message);
|
||||||
|
} else {
|
||||||
|
frm.set_value("max_benefits", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
frm.refresh_fields();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -82,14 +107,19 @@ var calculate_all = function(doc) {
|
|||||||
var tbl = doc.employee_benefits || [];
|
var tbl = doc.employee_benefits || [];
|
||||||
var pro_rata_dispensed_amount = 0;
|
var pro_rata_dispensed_amount = 0;
|
||||||
var total_amount = 0;
|
var total_amount = 0;
|
||||||
for(var i = 0; i < tbl.length; i++){
|
if (doc.max_benefits === 0) {
|
||||||
if(cint(tbl[i].amount) > 0) {
|
doc.employee_benefits = [];
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < tbl.length; i++) {
|
||||||
|
if (cint(tbl[i].amount) > 0) {
|
||||||
total_amount += flt(tbl[i].amount);
|
total_amount += flt(tbl[i].amount);
|
||||||
}
|
}
|
||||||
if(tbl[i].pay_against_benefit_claim != 1){
|
if (tbl[i].pay_against_benefit_claim != 1) {
|
||||||
pro_rata_dispensed_amount += flt(tbl[i].amount);
|
pro_rata_dispensed_amount += flt(tbl[i].amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
doc.total_amount = total_amount;
|
doc.total_amount = total_amount;
|
||||||
doc.remaining_benefit = doc.max_benefits - total_amount;
|
doc.remaining_benefit = doc.max_benefits - total_amount;
|
||||||
doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount;
|
doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount;
|
||||||
|
|||||||
@ -10,12 +10,14 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"employee",
|
"employee",
|
||||||
"employee_name",
|
"employee_name",
|
||||||
|
"currency",
|
||||||
"max_benefits",
|
"max_benefits",
|
||||||
"remaining_benefit",
|
"remaining_benefit",
|
||||||
"column_break_2",
|
"column_break_2",
|
||||||
"date",
|
"date",
|
||||||
"payroll_period",
|
"payroll_period",
|
||||||
"department",
|
"department",
|
||||||
|
"company",
|
||||||
"amended_from",
|
"amended_from",
|
||||||
"section_break_4",
|
"section_break_4",
|
||||||
"employee_benefits",
|
"employee_benefits",
|
||||||
@ -43,12 +45,14 @@
|
|||||||
"fieldname": "max_benefits",
|
"fieldname": "max_benefits",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Max Benefits (Yearly)",
|
"label": "Max Benefits (Yearly)",
|
||||||
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "remaining_benefit",
|
"fieldname": "remaining_benefit",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Remaining Benefits (Yearly)",
|
"label": "Remaining Benefits (Yearly)",
|
||||||
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -108,18 +112,38 @@
|
|||||||
"fieldname": "total_amount",
|
"fieldname": "total_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Total Amount",
|
"label": "Total Amount",
|
||||||
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "pro_rata_dispensed_amount",
|
"fieldname": "pro_rata_dispensed_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Dispensed Amount (Pro-rated)",
|
"label": "Dispensed Amount (Pro-rated)",
|
||||||
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Company:company:default_currency",
|
||||||
|
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Currency",
|
||||||
|
"options": "Currency",
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.company",
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-22 22:58:31.271922",
|
"modified": "2020-11-25 11:49:05.095101",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Application",
|
"name": "Employee Benefit Application",
|
||||||
|
|||||||
@ -33,8 +33,8 @@ class EmployeeBenefitApplication(Document):
|
|||||||
benefit_given = get_sal_slip_total_benefit_given(self.employee, payroll_period, component = benefit.earning_component)
|
benefit_given = get_sal_slip_total_benefit_given(self.employee, payroll_period, component = benefit.earning_component)
|
||||||
benefit_claim_remining = benefit_claimed - benefit_given
|
benefit_claim_remining = benefit_claimed - benefit_given
|
||||||
if benefit_claimed > 0 and benefit_claim_remining > benefit.amount:
|
if benefit_claimed > 0 and benefit_claim_remining > benefit.amount:
|
||||||
frappe.throw(_("An amount of {0} already claimed for the component {1},\
|
frappe.throw(_("An amount of {0} already claimed for the component {1}, set the amount equal or greater than {2}").format(
|
||||||
set the amount equal or greater than {2}").format(benefit_claimed, benefit.earning_component, benefit_claim_remining))
|
benefit_claimed, benefit.earning_component, benefit_claim_remining))
|
||||||
|
|
||||||
def validate_remaining_benefit_amount(self):
|
def validate_remaining_benefit_amount(self):
|
||||||
# check salary structure earnings have flexi component (sum of max_benefit_amount)
|
# check salary structure earnings have flexi component (sum of max_benefit_amount)
|
||||||
@ -62,11 +62,11 @@ class EmployeeBenefitApplication(Document):
|
|||||||
if pro_rata_amount == 0 and non_pro_rata_amount == 0:
|
if pro_rata_amount == 0 and non_pro_rata_amount == 0:
|
||||||
frappe.throw(_("Please add the remaining benefits {0} to any of the existing component").format(self.remaining_benefit))
|
frappe.throw(_("Please add the remaining benefits {0} to any of the existing component").format(self.remaining_benefit))
|
||||||
elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remaining_benefit):
|
elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remaining_benefit):
|
||||||
frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application \
|
frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application as pro-rata component").format(
|
||||||
as pro-rata component").format(non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount))
|
non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount))
|
||||||
elif non_pro_rata_amount == 0:
|
elif non_pro_rata_amount == 0:
|
||||||
frappe.throw(_("Please add the remaining benefits {0} to the application as \
|
frappe.throw(_("Please add the remaining benefits {0} to the application as pro-rata component").format(
|
||||||
pro-rata component").format(self.remaining_benefit))
|
self.remaining_benefit))
|
||||||
|
|
||||||
def validate_max_benefit_for_component(self):
|
def validate_max_benefit_for_component(self):
|
||||||
if self.employee_benefits:
|
if self.employee_benefits:
|
||||||
@ -115,7 +115,7 @@ def get_max_benefits_remaining(employee, on_date, payroll_period):
|
|||||||
if max_benefits and max_benefits > 0:
|
if max_benefits and max_benefits > 0:
|
||||||
have_depends_on_payment_days = False
|
have_depends_on_payment_days = False
|
||||||
per_day_amount_total = 0
|
per_day_amount_total = 0
|
||||||
payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[0]
|
payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[1]
|
||||||
payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
|
payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
|
||||||
|
|
||||||
# Get all salary slip flexi amount in the payroll period
|
# Get all salary slip flexi amount in the payroll period
|
||||||
@ -240,3 +240,16 @@ def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
else:
|
else:
|
||||||
frappe.throw(_("Salary Structure not found for employee {0} and date {1}")
|
frappe.throw(_("Salary Structure not found for employee {0} and date {1}")
|
||||||
.format(filters['employee'], filters['date']))
|
.format(filters['employee'], filters['date']))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_earning_components_max_benefits(employee, date, earning_component):
|
||||||
|
salary_structure = get_assigned_salary_structure(employee, date)
|
||||||
|
amount = frappe.db.sql("""
|
||||||
|
select amount
|
||||||
|
from `tabSalary Detail`
|
||||||
|
where parent = %s and is_flexible_benefit = 1
|
||||||
|
and salary_component = %s
|
||||||
|
order by name
|
||||||
|
""", salary_structure, earning_component)
|
||||||
|
|
||||||
|
return amount if amount else 0
|
||||||
@ -33,6 +33,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Max Benefit Amount",
|
"label": "Max Benefit Amount",
|
||||||
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -40,12 +41,13 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
|
"options": "currency",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-22 23:45:00.519134",
|
"modified": "2020-09-29 16:22:15.783854",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Application Detail",
|
"name": "Employee Benefit Application Detail",
|
||||||
|
|||||||
@ -12,5 +12,24 @@ frappe.ui.form.on('Employee Benefit Claim', {
|
|||||||
},
|
},
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
frm.set_value("earning_component", null);
|
frm.set_value("earning_component", null);
|
||||||
|
if (frm.doc.employee) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
|
||||||
|
args: {
|
||||||
|
employee: frm.doc.employee,
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
frm.set_value('currency', r.message);
|
||||||
|
frm.set_df_property('currency', 'hidden', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!frm.doc.earning_component) {
|
||||||
|
frm.doc.max_amount_eligible = null;
|
||||||
|
frm.doc.claimed_amount = null;
|
||||||
|
}
|
||||||
|
frm.refresh_fields();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,6 +12,8 @@
|
|||||||
"department",
|
"department",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"claim_date",
|
"claim_date",
|
||||||
|
"currency",
|
||||||
|
"company",
|
||||||
"benefit_type_and_amount",
|
"benefit_type_and_amount",
|
||||||
"earning_component",
|
"earning_component",
|
||||||
"max_amount_eligible",
|
"max_amount_eligible",
|
||||||
@ -76,6 +78,7 @@
|
|||||||
"fieldname": "max_amount_eligible",
|
"fieldname": "max_amount_eligible",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Max Amount Eligible",
|
"label": "Max Amount Eligible",
|
||||||
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -92,6 +95,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Claimed Amount",
|
"label": "Claimed Amount",
|
||||||
|
"options": "currency",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -119,11 +123,29 @@
|
|||||||
"fieldname": "attachments",
|
"fieldname": "attachments",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"label": "Attachments"
|
"label": "Attachments"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Company:company:default_currency",
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Currency",
|
||||||
|
"options": "Currency",
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.company",
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-22 23:01:50.791676",
|
"modified": "2020-11-25 11:49:56.097352",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Claim",
|
"name": "Employee Benefit Claim",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user