Merge branch 'develop' into do-not-manufacture-same-serial-no-two-develop
This commit is contained in:
commit
5e8e0037d6
4
.github/helper/documentation.py
vendored
4
.github/helper/documentation.py
vendored
@ -21,8 +21,8 @@ def docs_link_exists(body):
|
|||||||
if word.startswith('http') and uri_validator(word):
|
if word.startswith('http') and uri_validator(word):
|
||||||
parsed_url = urlparse(word)
|
parsed_url = urlparse(word)
|
||||||
if parsed_url.netloc == "github.com":
|
if parsed_url.netloc == "github.com":
|
||||||
_, org, repo, _type, ref = parsed_url.path.split('/')
|
parts = parsed_url.path.split('/')
|
||||||
if org == "frappe" and repo in docs_repos:
|
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -910,98 +910,8 @@
|
|||||||
},
|
},
|
||||||
"is_group": 1
|
"is_group": 1
|
||||||
},
|
},
|
||||||
"Passiva": {
|
"Passiva - Verbindlichkeiten": {
|
||||||
"root_type": "Liability",
|
"root_type": "Liability",
|
||||||
"A - Eigenkapital": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1,
|
|
||||||
"I - Gezeichnetes Kapital": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1,
|
|
||||||
"Gezeichnetes Kapital": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"account_number": "2900"
|
|
||||||
},
|
|
||||||
"Ausstehende Einlagen auf das gezeichnete Kapital": {
|
|
||||||
"account_number": "2910",
|
|
||||||
"is_group": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"II - Kapitalr\u00fccklage": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1,
|
|
||||||
"Kapitalr\u00fccklage": {
|
|
||||||
"account_number": "2920"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"III - Gewinnr\u00fccklagen": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"1 - gesetzliche R\u00fccklage": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1,
|
|
||||||
"Gesetzliche R\u00fccklage": {
|
|
||||||
"account_number": "2930"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1
|
|
||||||
},
|
|
||||||
"3 - satzungsm\u00e4\u00dfige R\u00fccklagen": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1,
|
|
||||||
"Satzungsm\u00e4\u00dfige R\u00fccklagen": {
|
|
||||||
"account_number": "2950"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"4 - andere Gewinnr\u00fccklagen": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1,
|
|
||||||
"Gewinnr\u00fccklagen aus den \u00dcbergangsvorschriften BilMoG": {
|
|
||||||
"is_group": 1,
|
|
||||||
"Gewinnr\u00fccklagen (BilMoG)": {
|
|
||||||
"account_number": "2963"
|
|
||||||
},
|
|
||||||
"Gewinnr\u00fccklagen aus Zuschreibung Sachanlageverm\u00f6gen (BilMoG)": {
|
|
||||||
"account_number": "2964"
|
|
||||||
},
|
|
||||||
"Gewinnr\u00fccklagen aus Zuschreibung Finanzanlageverm\u00f6gen (BilMoG)": {
|
|
||||||
"account_number": "2965"
|
|
||||||
},
|
|
||||||
"Gewinnr\u00fccklagen aus Aufl\u00f6sung der Sonderposten mit R\u00fccklageanteil (BilMoG)": {
|
|
||||||
"account_number": "2966"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Latente Steuern (Gewinnr\u00fccklage Haben) aus erfolgsneutralen Verrechnungen": {
|
|
||||||
"account_number": "2967"
|
|
||||||
},
|
|
||||||
"Latente Steuern (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
|
|
||||||
"account_number": "2968"
|
|
||||||
},
|
|
||||||
"Rechnungsabgrenzungsposten (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
|
|
||||||
"account_number": "2969"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"is_group": 1
|
|
||||||
},
|
|
||||||
"IV - Gewinnvortrag/Verlustvortrag": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1,
|
|
||||||
"Gewinnvortrag vor Verwendung": {
|
|
||||||
"account_number": "2970"
|
|
||||||
},
|
|
||||||
"Verlustvortrag vor Verwendung": {
|
|
||||||
"account_number": "2978"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": {
|
|
||||||
"account_type": "Equity",
|
|
||||||
"is_group": 1
|
|
||||||
},
|
|
||||||
"Einlagen stiller Gesellschafter": {
|
|
||||||
"account_number": "9295"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"B - R\u00fcckstellungen": {
|
"B - R\u00fcckstellungen": {
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
"1 - R\u00fcckstellungen f. Pensionen und \u00e4hnliche Verplicht.": {
|
"1 - R\u00fcckstellungen f. Pensionen und \u00e4hnliche Verplicht.": {
|
||||||
@ -1618,6 +1528,143 @@
|
|||||||
},
|
},
|
||||||
"is_group": 1
|
"is_group": 1
|
||||||
},
|
},
|
||||||
|
"Passiva - Eigenkapital": {
|
||||||
|
"root_type": "Equity",
|
||||||
|
"A - Eigenkapital": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1,
|
||||||
|
"I - Gezeichnetes Kapital": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1,
|
||||||
|
"Gezeichnetes Kapital": {
|
||||||
|
"account_number": "2900",
|
||||||
|
"account_type": "Equity"
|
||||||
|
},
|
||||||
|
"Gesch\u00e4ftsguthaben der verbleibenden Mitglieder": {
|
||||||
|
"account_number": "2901"
|
||||||
|
},
|
||||||
|
"Gesch\u00e4ftsguthaben der ausscheidenden Mitglieder": {
|
||||||
|
"account_number": "2902"
|
||||||
|
},
|
||||||
|
"Gesch\u00e4ftsguthaben aus gek\u00fcndigten Gesch\u00e4ftsanteilen": {
|
||||||
|
"account_number": "2903"
|
||||||
|
},
|
||||||
|
"R\u00fcckst\u00e4ndige f\u00e4llige Einzahlungen auf Gesch\u00e4ftsanteile, vermerkt": {
|
||||||
|
"account_number": "2906"
|
||||||
|
},
|
||||||
|
"Gegenkonto R\u00fcckst\u00e4ndige f\u00e4llige Einzahlungen auf Gesch\u00e4ftsanteile, vermerkt": {
|
||||||
|
"account_number": "2907"
|
||||||
|
},
|
||||||
|
"Kapitalerh\u00f6hung aus Gesellschaftsmitteln": {
|
||||||
|
"account_number": "2908"
|
||||||
|
},
|
||||||
|
"Ausstehende Einlagen auf das gezeichnete Kapital, nicht eingefordert": {
|
||||||
|
"account_number": "2910"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"II - Kapitalr\u00fccklage": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1,
|
||||||
|
"Kapitalr\u00fccklage": {
|
||||||
|
"account_number": "2920"
|
||||||
|
},
|
||||||
|
"Kapitalr\u00fccklage durch Ausgabe von Anteilen \u00fcber Nennbetrag": {
|
||||||
|
"account_number": "2925"
|
||||||
|
},
|
||||||
|
"Kapitalr\u00fccklage durch Ausgabe von Schuldverschreibungen": {
|
||||||
|
"account_number": "2926"
|
||||||
|
},
|
||||||
|
"Kapitalr\u00fccklage durch Zuzahlungen gegen Gew\u00e4hrung eines Vorzugs": {
|
||||||
|
"account_number": "2927"
|
||||||
|
},
|
||||||
|
"Kapitalr\u00fccklage durch Zuzahlungen in das Eigenkapital": {
|
||||||
|
"account_number": "2928"
|
||||||
|
},
|
||||||
|
"Nachschusskapital (Gegenkonto 1299)": {
|
||||||
|
"account_number": "2929"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"III - Gewinnr\u00fccklagen": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"1 - gesetzliche R\u00fccklage": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1,
|
||||||
|
"Gesetzliche R\u00fccklage": {
|
||||||
|
"account_number": "2930"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"2 - R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1,
|
||||||
|
"R\u00fccklage f. Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen": {
|
||||||
|
"account_number": "2935"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"3 - satzungsm\u00e4\u00dfige R\u00fccklagen": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1,
|
||||||
|
"Satzungsm\u00e4\u00dfige R\u00fccklagen": {
|
||||||
|
"account_number": "2950"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"4 - andere Gewinnr\u00fccklagen": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1,
|
||||||
|
"Andere Gewinnr\u00fccklagen": {
|
||||||
|
"account_number": "2960"
|
||||||
|
},
|
||||||
|
"Andere Gewinnr\u00fccklagen aus dem Erwerb eigener Anteile": {
|
||||||
|
"account_number": "2961"
|
||||||
|
},
|
||||||
|
"Eigenkapitalanteil von Wertaufholungen": {
|
||||||
|
"account_number": "2962"
|
||||||
|
},
|
||||||
|
"Gewinnr\u00fccklagen aus den \u00dcbergangsvorschriften BilMoG": {
|
||||||
|
"is_group": 1,
|
||||||
|
"Gewinnr\u00fccklagen (BilMoG)": {
|
||||||
|
"account_number": "2963"
|
||||||
|
},
|
||||||
|
"Gewinnr\u00fccklagen aus Zuschreibung Sachanlageverm\u00f6gen (BilMoG)": {
|
||||||
|
"account_number": "2964"
|
||||||
|
},
|
||||||
|
"Gewinnr\u00fccklagen aus Zuschreibung Finanzanlageverm\u00f6gen (BilMoG)": {
|
||||||
|
"account_number": "2965"
|
||||||
|
},
|
||||||
|
"Gewinnr\u00fccklagen aus Aufl\u00f6sung der Sonderposten mit R\u00fccklageanteil (BilMoG)": {
|
||||||
|
"account_number": "2966"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Latente Steuern (Gewinnr\u00fccklage Haben) aus erfolgsneutralen Verrechnungen": {
|
||||||
|
"account_number": "2967"
|
||||||
|
},
|
||||||
|
"Latente Steuern (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
|
||||||
|
"account_number": "2968"
|
||||||
|
},
|
||||||
|
"Rechnungsabgrenzungsposten (Gewinnr\u00fccklage Soll) aus erfolgsneutralen Verrechnungen": {
|
||||||
|
"account_number": "2969"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is_group": 1
|
||||||
|
},
|
||||||
|
"IV - Gewinnvortrag/Verlustvortrag": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1,
|
||||||
|
"Gewinnvortrag vor Verwendung": {
|
||||||
|
"account_number": "2970"
|
||||||
|
},
|
||||||
|
"Verlustvortrag vor Verwendung": {
|
||||||
|
"account_number": "2978"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"V - Jahres\u00fcberschu\u00df/Jahresfehlbetrag": {
|
||||||
|
"account_type": "Equity",
|
||||||
|
"is_group": 1
|
||||||
|
},
|
||||||
|
"Einlagen stiller Gesellschafter": {
|
||||||
|
"account_number": "9295"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"1 - Umsatzerl\u00f6se": {
|
"1 - Umsatzerl\u00f6se": {
|
||||||
"root_type": "Income",
|
"root_type": "Income",
|
||||||
"is_group": 1,
|
"is_group": 1,
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
@ -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"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import g
|
|||||||
class AssetValueAdjustment(Document):
|
class AssetValueAdjustment(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_date()
|
self.validate_date()
|
||||||
self.set_difference_amount()
|
|
||||||
self.set_current_asset_value()
|
self.set_current_asset_value()
|
||||||
|
self.set_difference_amount()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.make_depreciation_entry()
|
self.make_depreciation_entry()
|
||||||
@ -53,6 +53,7 @@ class AssetValueAdjustment(Document):
|
|||||||
je.posting_date = self.date
|
je.posting_date = self.date
|
||||||
je.company = self.company
|
je.company = self.company
|
||||||
je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount)
|
je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount)
|
||||||
|
je.finance_book = self.finance_book
|
||||||
|
|
||||||
credit_entry = {
|
credit_entry = {
|
||||||
"account": accumulated_depreciation_account,
|
"account": accumulated_depreciation_account,
|
||||||
|
@ -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):
|
||||||
@ -252,26 +254,36 @@ class StatusUpdater(Document):
|
|||||||
if not args.get("second_source_extra_cond"):
|
if not args.get("second_source_extra_cond"):
|
||||||
args["second_source_extra_cond"] = ""
|
args["second_source_extra_cond"] = ""
|
||||||
|
|
||||||
args['second_source_condition'] = """ + ifnull((select sum(%(second_source_field)s)
|
args['second_source_condition'] = frappe.db.sql(""" select ifnull((select sum(%(second_source_field)s)
|
||||||
from `tab%(second_source_dt)s`
|
from `tab%(second_source_dt)s`
|
||||||
where `%(second_join_field)s`="%(detail_id)s"
|
where `%(second_join_field)s`="%(detail_id)s"
|
||||||
and (`tab%(second_source_dt)s`.docstatus=1) %(second_source_extra_cond)s FOR UPDATE), 0)""" % args
|
and (`tab%(second_source_dt)s`.docstatus=1)
|
||||||
|
%(second_source_extra_cond)s), 0) """ % args)[0][0]
|
||||||
|
|
||||||
if args['detail_id']:
|
if args['detail_id']:
|
||||||
if not args.get("extra_cond"): args["extra_cond"] = ""
|
if not args.get("extra_cond"): args["extra_cond"] = ""
|
||||||
|
|
||||||
frappe.db.sql("""update `tab%(target_dt)s`
|
args["source_dt_value"] = frappe.db.sql("""
|
||||||
set %(target_field)s = (
|
|
||||||
(select ifnull(sum(%(source_field)s), 0)
|
(select ifnull(sum(%(source_field)s), 0)
|
||||||
from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s"
|
from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s"
|
||||||
and (docstatus=1 %(cond)s) %(extra_cond)s)
|
and (docstatus=1 %(cond)s) %(extra_cond)s)
|
||||||
%(second_source_condition)s
|
""" % args)[0][0] or 0.0
|
||||||
)
|
|
||||||
%(update_modified)s
|
if args['second_source_condition']:
|
||||||
|
args["source_dt_value"] += flt(args['second_source_condition'])
|
||||||
|
|
||||||
|
frappe.db.sql("""update `tab%(target_dt)s`
|
||||||
|
set %(target_field)s = %(source_dt_value)s %(update_modified)s
|
||||||
where name='%(detail_id)s'""" % args)
|
where name='%(detail_id)s'""" % args)
|
||||||
|
|
||||||
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
|
||||||
|
@ -126,7 +126,7 @@ class Appointment(Document):
|
|||||||
add_assignemnt({
|
add_assignemnt({
|
||||||
'doctype': self.doctype,
|
'doctype': self.doctype,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'assign_to': existing_assignee
|
'assign_to': [existing_assignee]
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
if self._assign:
|
if self._assign:
|
||||||
@ -139,7 +139,7 @@ class Appointment(Document):
|
|||||||
add_assignemnt({
|
add_assignemnt({
|
||||||
'doctype': self.doctype,
|
'doctype': self.doctype,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'assign_to': agent
|
'assign_to': [agent]
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
@ -260,6 +260,15 @@ def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings):
|
|||||||
"""Shipping lines represents the shipping details,
|
"""Shipping lines represents the shipping details,
|
||||||
each such shipping detail consists of a list of tax_lines"""
|
each such shipping detail consists of a list of tax_lines"""
|
||||||
for shipping_charge in shipping_lines:
|
for shipping_charge in shipping_lines:
|
||||||
|
if shipping_charge.get("price"):
|
||||||
|
taxes.append({
|
||||||
|
"charge_type": _("Actual"),
|
||||||
|
"account_head": get_tax_account_head(shipping_charge),
|
||||||
|
"description": shipping_charge["title"],
|
||||||
|
"tax_amount": shipping_charge["price"],
|
||||||
|
"cost_center": shopify_settings.cost_center
|
||||||
|
})
|
||||||
|
|
||||||
for tax in shipping_charge.get("tax_lines"):
|
for tax in shipping_charge.get("tax_lines"):
|
||||||
taxes.append({
|
taxes.append({
|
||||||
"charge_type": _("Actual"),
|
"charge_type": _("Actual"),
|
||||||
|
@ -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()
|
||||||
|
@ -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},
|
||||||
|
@ -18,13 +18,18 @@ frappe.ui.form.on('Employee Advance', {
|
|||||||
if (!frm.doc.employee) {
|
if (!frm.doc.employee) {
|
||||||
frappe.msgprint(__("Please select employee first"));
|
frappe.msgprint(__("Please select employee first"));
|
||||||
}
|
}
|
||||||
var company_currency = erpnext.get_currency(frm.doc.company);
|
let company_currency = erpnext.get_currency(frm.doc.company);
|
||||||
|
let currencies = [company_currency];
|
||||||
|
if (frm.doc.currency && (frm.doc.currency != company_currency)) {
|
||||||
|
currencies.push(frm.doc.currency);
|
||||||
|
}
|
||||||
|
|
||||||
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]],
|
"account_currency": ["in", currencies],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -181,6 +186,7 @@ frappe.ui.form.on('Employee Advance', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
currency: function(frm) {
|
currency: function(frm) {
|
||||||
|
if (frm.doc.currency) {
|
||||||
var from_currency = frm.doc.currency;
|
var from_currency = frm.doc.currency;
|
||||||
var company_currency;
|
var company_currency;
|
||||||
if (!frm.doc.company) {
|
if (!frm.doc.company) {
|
||||||
@ -196,6 +202,7 @@ frappe.ui.form.on('Employee Advance', {
|
|||||||
frm.set_df_property("exchange_rate", "description", "" );
|
frm.set_df_property("exchange_rate", "description", "" );
|
||||||
}
|
}
|
||||||
frm.refresh_fields();
|
frm.refresh_fields();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
set_exchange_rate: function(frm, from_currency, company_currency) {
|
set_exchange_rate: function(frm, from_currency, company_currency) {
|
||||||
|
@ -8,7 +8,17 @@ frappe.views.calendar["Job Card"] = {
|
|||||||
"allDay": "allDay",
|
"allDay": "allDay",
|
||||||
"progress": "progress"
|
"progress": "progress"
|
||||||
},
|
},
|
||||||
gantt: true,
|
gantt: {
|
||||||
|
field_map: {
|
||||||
|
"start": "started_time",
|
||||||
|
"end": "started_time",
|
||||||
|
"id": "name",
|
||||||
|
"title": "subject",
|
||||||
|
"color": "color",
|
||||||
|
"allDay": "allDay",
|
||||||
|
"progress": "progress"
|
||||||
|
}
|
||||||
|
},
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
@ -491,6 +491,39 @@ class TestWorkOrder(unittest.TestCase):
|
|||||||
work_order1.save()
|
work_order1.save()
|
||||||
self.assertEqual(work_order1.operations[0].time_in_mins, 40.0)
|
self.assertEqual(work_order1.operations[0].time_in_mins, 40.0)
|
||||||
|
|
||||||
|
def test_partial_material_consumption(self):
|
||||||
|
frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 1)
|
||||||
|
wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4)
|
||||||
|
|
||||||
|
ste_cancel_list = []
|
||||||
|
ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item",
|
||||||
|
target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0)
|
||||||
|
ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
||||||
|
target="_Test Warehouse - _TC", qty=20, basic_rate=1000.0)
|
||||||
|
|
||||||
|
ste_cancel_list.extend([ste1, ste2])
|
||||||
|
|
||||||
|
s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 4))
|
||||||
|
s.submit()
|
||||||
|
ste_cancel_list.append(s)
|
||||||
|
|
||||||
|
ste1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
|
||||||
|
ste1.submit()
|
||||||
|
ste_cancel_list.append(ste1)
|
||||||
|
|
||||||
|
print(wo_order.name)
|
||||||
|
ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2))
|
||||||
|
self.assertEquals(ste3.fg_completed_qty, 2)
|
||||||
|
|
||||||
|
expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4}
|
||||||
|
for row in ste3.items:
|
||||||
|
self.assertEquals(row.qty, expected_qty.get(row.item_code))
|
||||||
|
|
||||||
|
for ste_doc in ste_cancel_list:
|
||||||
|
ste_doc.cancel()
|
||||||
|
|
||||||
|
frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 0)
|
||||||
|
|
||||||
def get_scrap_item_details(bom_no):
|
def get_scrap_item_details(bom_no):
|
||||||
scrap_items = {}
|
scrap_items = {}
|
||||||
for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
|
for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
|
||||||
|
@ -545,7 +545,8 @@ erpnext.work_order = {
|
|||||||
var tbl = frm.doc.required_items || [];
|
var tbl = frm.doc.required_items || [];
|
||||||
var tbl_lenght = tbl.length;
|
var tbl_lenght = tbl.length;
|
||||||
for (var i = 0, len = tbl_lenght; i < len; i++) {
|
for (var i = 0, len = tbl_lenght; i < len; i++) {
|
||||||
if (flt(frm.doc.required_items[i].required_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
|
let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty;
|
||||||
|
if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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]
|
||||||
|
@ -59,7 +59,7 @@ class Member(Document):
|
|||||||
frappe.msgprint(_("A customer is already linked to this Member"))
|
frappe.msgprint(_("A customer is already linked to this Member"))
|
||||||
cust = create_customer(frappe._dict({
|
cust = create_customer(frappe._dict({
|
||||||
'fullname': self.member_name,
|
'fullname': self.member_name,
|
||||||
'email': self.email_id or self.user,
|
'email': self.email_id or self.email,
|
||||||
'phone': None
|
'phone': None
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -450,7 +450,6 @@ erpnext.patches.v8_9.set_member_party_type
|
|||||||
erpnext.patches.v9_0.add_user_to_child_table_in_pos_profile
|
erpnext.patches.v9_0.add_user_to_child_table_in_pos_profile
|
||||||
erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order
|
erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order
|
||||||
erpnext.patches.v9_0.student_admission_childtable_migrate
|
erpnext.patches.v9_0.student_admission_childtable_migrate
|
||||||
erpnext.patches.v9_0.fix_subscription_next_date #2017-10-23
|
|
||||||
erpnext.patches.v9_0.add_healthcare_domain
|
erpnext.patches.v9_0.add_healthcare_domain
|
||||||
erpnext.patches.v9_0.set_variant_item_description
|
erpnext.patches.v9_0.set_variant_item_description
|
||||||
erpnext.patches.v9_0.set_uoms_in_variant_field
|
erpnext.patches.v9_0.set_uoms_in_variant_field
|
||||||
@ -691,6 +690,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
|
||||||
@ -738,3 +738,5 @@ 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.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
|
||||||
|
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:
|
||||||
|
@ -5,6 +5,8 @@ from frappe.utils import nowdate
|
|||||||
from erpnext.accounts.doctype.account.test_account import create_account
|
from erpnext.accounts.doctype.account.test_account import create_account
|
||||||
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
|
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
|
||||||
from erpnext.loan_management.doctype.loan.loan import make_repayment_entry
|
from erpnext.loan_management.doctype.loan.loan import make_repayment_entry
|
||||||
|
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import get_accrued_interest_entries
|
||||||
|
from frappe.model.naming import make_autoname
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
|
|
||||||
@ -18,15 +20,29 @@ def execute():
|
|||||||
frappe.reload_doc('loan_management', 'doctype', 'loan_repayment_detail')
|
frappe.reload_doc('loan_management', 'doctype', 'loan_repayment_detail')
|
||||||
frappe.reload_doc('loan_management', 'doctype', 'loan_interest_accrual')
|
frappe.reload_doc('loan_management', 'doctype', 'loan_interest_accrual')
|
||||||
frappe.reload_doc('accounts', 'doctype', 'gl_entry')
|
frappe.reload_doc('accounts', 'doctype', 'gl_entry')
|
||||||
|
frappe.reload_doc('accounts', 'doctype', 'journal_entry_account')
|
||||||
|
|
||||||
updated_loan_types = []
|
updated_loan_types = []
|
||||||
|
loans_to_close = []
|
||||||
|
|
||||||
|
# Update old loan status as closed
|
||||||
|
if frappe.db.has_column('Repayment Schedule', 'paid'):
|
||||||
|
loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule`
|
||||||
|
where paid = 0 and docstatus = 1""", as_dict=1)
|
||||||
|
|
||||||
|
loans_to_close = [d.parent for d in loans_list]
|
||||||
|
|
||||||
|
if loans_to_close:
|
||||||
|
frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close))
|
||||||
|
|
||||||
loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment',
|
loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment',
|
||||||
'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'])
|
'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'],
|
||||||
|
filters={'docstatus': 1, 'status': ('!=', 'Closed')})
|
||||||
|
|
||||||
for loan in loans:
|
for loan in loans:
|
||||||
# Update details in Loan Types and Loan
|
# Update details in Loan Types and Loan
|
||||||
loan_type_company = frappe.db.get_value('Loan Type', loan.loan_type, 'company')
|
loan_type_company = frappe.db.get_value('Loan Type', loan.loan_type, 'company')
|
||||||
|
loan_type = loan.loan_type
|
||||||
|
|
||||||
group_income_account = frappe.get_value('Account', {'company': loan.company,
|
group_income_account = frappe.get_value('Account', {'company': loan.company,
|
||||||
'is_group': 1, 'root_type': 'Income', 'account_name': _('Indirect Income')})
|
'is_group': 1, 'root_type': 'Income', 'account_name': _('Indirect Income')})
|
||||||
@ -38,7 +54,26 @@ def execute():
|
|||||||
penalty_account = create_account(company=loan.company, account_type='Income Account',
|
penalty_account = create_account(company=loan.company, account_type='Income Account',
|
||||||
account_name='Penalty Account', parent_account=group_income_account)
|
account_name='Penalty Account', parent_account=group_income_account)
|
||||||
|
|
||||||
if not loan_type_company:
|
# Same loan type used for multiple companies
|
||||||
|
if loan_type_company and loan_type_company != loan.company:
|
||||||
|
# get loan type for appropriate company
|
||||||
|
loan_type_name = frappe.get_value('Loan Type', {'company': loan.company,
|
||||||
|
'mode_of_payment': loan.mode_of_payment, 'loan_account': loan.loan_account,
|
||||||
|
'payment_account': loan.payment_account, 'interest_income_account': loan.interest_income_account,
|
||||||
|
'penalty_income_account': loan.penalty_income_account}, 'name')
|
||||||
|
|
||||||
|
if not loan_type_name:
|
||||||
|
loan_type_name = create_loan_type(loan, loan_type_name, penalty_account)
|
||||||
|
|
||||||
|
# update loan type in loan
|
||||||
|
frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name,
|
||||||
|
loan.name))
|
||||||
|
|
||||||
|
loan_type = loan_type_name
|
||||||
|
if loan_type_name not in updated_loan_types:
|
||||||
|
updated_loan_types.append(loan_type_name)
|
||||||
|
|
||||||
|
elif not loan_type_company:
|
||||||
loan_type_doc = frappe.get_doc('Loan Type', loan.loan_type)
|
loan_type_doc = frappe.get_doc('Loan Type', loan.loan_type)
|
||||||
loan_type_doc.is_term_loan = 1
|
loan_type_doc.is_term_loan = 1
|
||||||
loan_type_doc.company = loan.company
|
loan_type_doc.company = loan.company
|
||||||
@ -49,8 +84,9 @@ def execute():
|
|||||||
loan_type_doc.penalty_income_account = penalty_account
|
loan_type_doc.penalty_income_account = penalty_account
|
||||||
loan_type_doc.submit()
|
loan_type_doc.submit()
|
||||||
updated_loan_types.append(loan.loan_type)
|
updated_loan_types.append(loan.loan_type)
|
||||||
|
loan_type = loan.loan_type
|
||||||
|
|
||||||
if loan.loan_type in updated_loan_types:
|
if loan_type in updated_loan_types:
|
||||||
if loan.status == 'Fully Disbursed':
|
if loan.status == 'Fully Disbursed':
|
||||||
status = 'Disbursed'
|
status = 'Disbursed'
|
||||||
elif loan.status == 'Repaid/Closed':
|
elif loan.status == 'Repaid/Closed':
|
||||||
@ -64,25 +100,48 @@ def execute():
|
|||||||
'status': status
|
'status': status
|
||||||
})
|
})
|
||||||
|
|
||||||
process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan.loan_type,
|
process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type,
|
||||||
loan=loan.name)
|
loan=loan.name)
|
||||||
|
|
||||||
payments = frappe.db.sql(''' SELECT j.name, a.debit, a.debit_in_account_currency, j.posting_date
|
|
||||||
FROM `tabJournal Entry` j, `tabJournal Entry Account` a
|
|
||||||
WHERE a.parent = j.name and a.reference_type='Loan' and a.reference_name = %s
|
|
||||||
and account = %s
|
|
||||||
''', (loan.name, loan.loan_account), as_dict=1)
|
|
||||||
|
|
||||||
for payment in payments:
|
if frappe.db.has_column('Repayment Schedule', 'paid'):
|
||||||
repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant,
|
total_principal, total_interest = frappe.db.get_value('Repayment Schedule', {'paid': 1, 'parent': loan.name},
|
||||||
loan.loan_type, loan.company)
|
['sum(principal_amount) as total_principal', 'sum(interest_amount) as total_interest'])
|
||||||
|
|
||||||
repayment_entry.amount_paid = payment.debit_in_account_currency
|
accrued_entries = get_accrued_interest_entries(loan.name)
|
||||||
repayment_entry.posting_date = payment.posting_date
|
for entry in accrued_entries:
|
||||||
repayment_entry.save()
|
interest_paid = 0
|
||||||
repayment_entry.submit()
|
principal_paid = 0
|
||||||
|
|
||||||
jv = frappe.get_doc('Journal Entry', payment.name)
|
if total_interest > entry.interest_amount:
|
||||||
jv.flags.ignore_links = True
|
interest_paid = entry.interest_amount
|
||||||
jv.cancel()
|
else:
|
||||||
|
interest_paid = total_interest
|
||||||
|
|
||||||
|
if total_principal > entry.payable_principal_amount:
|
||||||
|
principal_paid = entry.payable_principal_amount
|
||||||
|
else:
|
||||||
|
principal_paid = total_principal
|
||||||
|
|
||||||
|
frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
|
||||||
|
SET paid_principal_amount = `paid_principal_amount` + %s,
|
||||||
|
paid_interest_amount = `paid_interest_amount` + %s
|
||||||
|
WHERE name = %s""",
|
||||||
|
(principal_paid, interest_paid, entry.name))
|
||||||
|
|
||||||
|
total_principal -= principal_paid
|
||||||
|
total_interest -= interest_paid
|
||||||
|
|
||||||
|
def create_loan_type(loan, loan_type_name, penalty_account):
|
||||||
|
loan_type_doc = frappe.new_doc('Loan Type')
|
||||||
|
loan_type_doc.loan_name = make_autoname("Loan Type-.####")
|
||||||
|
loan_type_doc.is_term_loan = 1
|
||||||
|
loan_type_doc.company = loan.company
|
||||||
|
loan_type_doc.mode_of_payment = loan.mode_of_payment
|
||||||
|
loan_type_doc.payment_account = loan.payment_account
|
||||||
|
loan_type_doc.loan_account = loan.loan_account
|
||||||
|
loan_type_doc.interest_income_account = loan.interest_income_account
|
||||||
|
loan_type_doc.penalty_income_account = penalty_account
|
||||||
|
loan_type_doc.submit()
|
||||||
|
|
||||||
|
return loan_type_doc.name
|
||||||
|
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)
|
@ -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,
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
# Copyright (c) 2017, Frappe and Contributors
|
|
||||||
# License: GNU General Public License v3. See license.txt
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
|
||||||
from frappe.utils import getdate
|
|
||||||
from frappe.automation.doctype.auto_repeat.auto_repeat import get_next_schedule_date
|
|
||||||
|
|
||||||
def execute():
|
|
||||||
frappe.reload_doc('accounts', 'doctype', 'subscription')
|
|
||||||
fields = ["name", "reference_doctype", "reference_document",
|
|
||||||
"start_date", "frequency", "repeat_on_day"]
|
|
||||||
|
|
||||||
for d in fields:
|
|
||||||
if not frappe.db.has_column('Subscription', d):
|
|
||||||
return
|
|
||||||
|
|
||||||
doctypes = ('Purchase Order', 'Sales Order', 'Purchase Invoice', 'Sales Invoice')
|
|
||||||
for data in frappe.get_all('Subscription',
|
|
||||||
fields = fields,
|
|
||||||
filters = {'reference_doctype': ('in', doctypes), 'docstatus': 1}):
|
|
||||||
|
|
||||||
recurring_id = frappe.db.get_value(data.reference_doctype, data.reference_document, "recurring_id")
|
|
||||||
if recurring_id:
|
|
||||||
frappe.db.sql("update `tab{0}` set subscription=%s where recurring_id=%s"
|
|
||||||
.format(data.reference_doctype), (data.name, recurring_id))
|
|
||||||
|
|
||||||
date_field = 'transaction_date'
|
|
||||||
if data.reference_doctype in ['Sales Invoice', 'Purchase Invoice']:
|
|
||||||
date_field = 'posting_date'
|
|
||||||
|
|
||||||
start_date = frappe.db.get_value(data.reference_doctype, data.reference_document, date_field)
|
|
||||||
|
|
||||||
if start_date and getdate(start_date) != getdate(data.start_date):
|
|
||||||
last_ref_date = frappe.db.sql("""
|
|
||||||
select {0}
|
|
||||||
from `tab{1}`
|
|
||||||
where subscription=%s and docstatus < 2
|
|
||||||
order by creation desc
|
|
||||||
limit 1
|
|
||||||
""".format(date_field, data.reference_doctype), data.name)[0][0]
|
|
||||||
|
|
||||||
next_schedule_date = get_next_schedule_date(last_ref_date, data.frequency, data.repeat_on_day)
|
|
||||||
|
|
||||||
frappe.db.set_value("Subscription", data.name, {
|
|
||||||
"start_date": start_date,
|
|
||||||
"next_schedule_date": next_schedule_date
|
|
||||||
}, None)
|
|
@ -12,14 +12,6 @@ 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) {
|
employee: function(frm) {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
"employee_benefits",
|
"employee_benefits",
|
||||||
"totals",
|
"totals",
|
||||||
"total_amount",
|
"total_amount",
|
||||||
|
"column_break",
|
||||||
"pro_rata_dispensed_amount"
|
"pro_rata_dispensed_amount"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -139,11 +140,15 @@
|
|||||||
"label": "Company",
|
"label": "Company",
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-25 11:49:05.095101",
|
"modified": "2020-12-14 15:52:08.566418",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Application",
|
"name": "Employee Benefit Application",
|
||||||
|
@ -11,11 +11,11 @@ frappe.ui.form.on('Employee Incentive', {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!frm.doc.currency) return;
|
if (!frm.doc.company) return;
|
||||||
frm.set_query("salary_component", function() {
|
frm.set_query("salary_component", function() {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
|
query: "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
|
||||||
filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company}
|
filters: {type: "earning", company: frm.doc.company}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -289,7 +289,9 @@ class PayrollEntry(Document):
|
|||||||
jv_name = journal_entry.name
|
jv_name = journal_entry.name
|
||||||
self.update_salary_slip_status(jv_name = jv_name)
|
self.update_salary_slip_status(jv_name = jv_name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if type(e) in (str, list, tuple):
|
||||||
frappe.msgprint(e)
|
frappe.msgprint(e)
|
||||||
|
raise
|
||||||
|
|
||||||
return jv_name
|
return jv_name
|
||||||
|
|
||||||
|
@ -4,9 +4,13 @@
|
|||||||
frappe.ui.form.on('Retention Bonus', {
|
frappe.ui.form.on('Retention Bonus', {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("employee", function() {
|
frm.set_query("employee", function() {
|
||||||
|
if (!frm.doc.company) {
|
||||||
|
frappe.msgprint(__("Please Select Company First"));
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"status": "Active"
|
"status": "Active",
|
||||||
|
"company": frm.doc.company
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -55,17 +55,17 @@ frappe.ui.form.on('Salary Structure', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
set_earning_deduction_component: function(frm) {
|
set_earning_deduction_component: function(frm) {
|
||||||
if(!frm.doc.currency && !frm.doc.company) return;
|
if(!frm.doc.company) return;
|
||||||
frm.set_query("salary_component", "earnings", function() {
|
frm.set_query("salary_component", "earnings", function() {
|
||||||
return {
|
return {
|
||||||
query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
|
query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
|
||||||
filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company}
|
filters: {type: "earning", company: frm.doc.company}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
frm.set_query("salary_component", "deductions", function() {
|
frm.set_query("salary_component", "deductions", function() {
|
||||||
return {
|
return {
|
||||||
query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
|
query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components",
|
||||||
filters: {type: "deduction", currency: frm.doc.currency, company: frm.doc.company}
|
filters: {type: "deduction", company: frm.doc.company}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -74,7 +74,6 @@ frappe.ui.form.on('Salary Structure', {
|
|||||||
currency: function(frm) {
|
currency: function(frm) {
|
||||||
calculate_totals(frm.doc);
|
calculate_totals(frm.doc);
|
||||||
frm.trigger("set_dynamic_labels")
|
frm.trigger("set_dynamic_labels")
|
||||||
frm.trigger('set_earning_deduction_component');
|
|
||||||
frm.refresh()
|
frm.refresh()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ def get_employees(salary_structure):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, filters):
|
def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, filters):
|
||||||
if len(filters) < 3:
|
if len(filters) < 2:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
|
@ -189,6 +189,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
|
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
|
||||||
item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
|
item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
|
||||||
|
item.received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(item.received_qty);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._super(doc, cdt, cdn);
|
this._super(doc, cdt, cdn);
|
||||||
@ -293,69 +294,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
this.get_terms();
|
this.get_terms();
|
||||||
},
|
},
|
||||||
|
|
||||||
link_to_mrs: function() {
|
|
||||||
var my_items = [];
|
|
||||||
for (var i in cur_frm.doc.items) {
|
|
||||||
if(!cur_frm.doc.items[i].material_request){
|
|
||||||
my_items.push(cur_frm.doc.items[i].item_code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.buying.utils.get_linked_material_requests",
|
|
||||||
args:{
|
|
||||||
items: my_items
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if(!r.message || r.message.length == 0) {
|
|
||||||
frappe.throw(__("No pending Material Requests found to link for the given items."))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var i = 0;
|
|
||||||
var item_length = cur_frm.doc.items.length;
|
|
||||||
while (i < item_length) {
|
|
||||||
var qty = cur_frm.doc.items[i].qty;
|
|
||||||
(r.message[0] || []).forEach(function(d) {
|
|
||||||
if (d.qty > 0 && qty > 0 && cur_frm.doc.items[i].item_code == d.item_code && !cur_frm.doc.items[i].material_request_item)
|
|
||||||
{
|
|
||||||
cur_frm.doc.items[i].material_request = d.mr_name;
|
|
||||||
cur_frm.doc.items[i].material_request_item = d.mr_item;
|
|
||||||
var my_qty = Math.min(qty, d.qty);
|
|
||||||
qty = qty - my_qty;
|
|
||||||
d.qty = d.qty - my_qty;
|
|
||||||
cur_frm.doc.items[i].stock_qty = my_qty*cur_frm.doc.items[i].conversion_factor;
|
|
||||||
cur_frm.doc.items[i].qty = my_qty;
|
|
||||||
|
|
||||||
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")");
|
|
||||||
if (qty > 0)
|
|
||||||
{
|
|
||||||
frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
|
|
||||||
var newrow = frappe.model.add_child(cur_frm.doc, cur_frm.doc.items[i].doctype, "items");
|
|
||||||
item_length++;
|
|
||||||
|
|
||||||
for (var key in cur_frm.doc.items[i])
|
|
||||||
{
|
|
||||||
newrow[key] = cur_frm.doc.items[i][key];
|
|
||||||
}
|
|
||||||
|
|
||||||
newrow.idx = item_length;
|
|
||||||
newrow["stock_qty"] = newrow.conversion_factor*qty;
|
|
||||||
newrow["qty"] = qty;
|
|
||||||
|
|
||||||
newrow["material_request"] = "";
|
|
||||||
newrow["material_request_item"] = "";
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
refresh_field("items");
|
|
||||||
//cur_frm.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
update_auto_repeat_reference: function(doc) {
|
update_auto_repeat_reference: function(doc) {
|
||||||
if (doc.auto_repeat) {
|
if (doc.auto_repeat) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
@ -421,6 +359,62 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
cur_frm.add_fetch('project', 'cost_center', 'cost_center');
|
cur_frm.add_fetch('project', 'cost_center', 'cost_center');
|
||||||
|
|
||||||
|
erpnext.buying.link_to_mrs = function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.buying.utils.get_linked_material_requests",
|
||||||
|
args:{
|
||||||
|
items: frm.doc.items.map((item) => item.item_code)
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (!r.message || r.message.length == 0) {
|
||||||
|
frappe.throw({
|
||||||
|
message: __("No pending Material Requests found to link for the given items."),
|
||||||
|
title: __("Note")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var item_length = frm.doc.items.length;
|
||||||
|
for (let item of frm.doc.items) {
|
||||||
|
var qty = item.qty;
|
||||||
|
(r.message[0] || []).forEach(function(d) {
|
||||||
|
if (d.qty > 0 && qty > 0 && item.item_code == d.item_code && !item.material_request_item)
|
||||||
|
{
|
||||||
|
item.material_request = d.mr_name;
|
||||||
|
item.material_request_item = d.mr_item;
|
||||||
|
var my_qty = Math.min(qty, d.qty);
|
||||||
|
qty = qty - my_qty;
|
||||||
|
d.qty = d.qty - my_qty;
|
||||||
|
item.stock_qty = my_qty*item.conversion_factor;
|
||||||
|
item.qty = my_qty;
|
||||||
|
|
||||||
|
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + item.idx + ")");
|
||||||
|
if (qty > 0)
|
||||||
|
{
|
||||||
|
frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
|
||||||
|
var newrow = frappe.model.add_child(frm.doc, item.doctype, "items");
|
||||||
|
item_length++;
|
||||||
|
|
||||||
|
for (var key in item)
|
||||||
|
{
|
||||||
|
newrow[key] = item[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
newrow.idx = item_length;
|
||||||
|
newrow["stock_qty"] = newrow.conversion_factor*qty;
|
||||||
|
newrow["qty"] = qty;
|
||||||
|
|
||||||
|
newrow["material_request"] = "";
|
||||||
|
newrow["material_request_item"] = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
refresh_field("items");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
erpnext.buying.get_default_bom = function(frm) {
|
erpnext.buying.get_default_bom = function(frm) {
|
||||||
$.each(frm.doc["items"] || [], function(i, d) {
|
$.each(frm.doc["items"] || [], function(i, d) {
|
||||||
if (d.item_code && d.bom === "") {
|
if (d.item_code && d.bom === "") {
|
||||||
|
@ -609,6 +609,15 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
|||||||
this.calculate_outstanding_amount(update_paid_amount);
|
this.calculate_outstanding_amount(update_paid_amount);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
is_internal_invoice: function() {
|
||||||
|
if (['Sales Invoice', 'Purchase Invoice'].includes(this.frm.doc.doctype)) {
|
||||||
|
if (this.frm.doc.company === this.frm.doc.represents_company) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
calculate_outstanding_amount: function(update_paid_amount) {
|
calculate_outstanding_amount: function(update_paid_amount) {
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice
|
// paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice
|
||||||
@ -617,7 +626,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
|||||||
this.calculate_paid_amount();
|
this.calculate_paid_amount();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.frm.doc.is_return || this.frm.doc.docstatus > 0) return;
|
if (this.frm.doc.is_return || (this.frm.doc.docstatus > 0) || this.is_internal_invoice()) return;
|
||||||
|
|
||||||
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]);
|
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]);
|
||||||
|
|
||||||
|
@ -408,7 +408,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
|
|
||||||
show_description(row_to_modify.idx, row_to_modify.item_code);
|
show_description(row_to_modify.idx, row_to_modify.item_code);
|
||||||
|
|
||||||
this.frm.from_barcode = true;
|
this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1;
|
||||||
frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, {
|
frappe.model.set_value(row_to_modify.doctype, row_to_modify.name, {
|
||||||
item_code: data.item_code,
|
item_code: data.item_code,
|
||||||
qty: (row_to_modify.qty || 0) + 1
|
qty: (row_to_modify.qty || 0) + 1
|
||||||
@ -492,7 +492,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
d.item_code = "";
|
d.item_code = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.frm.from_barcode = true;
|
this.frm.from_barcode = this.frm.from_barcode ? this.frm.from_barcode + 1 : 1;
|
||||||
this.item_code(doc, cdt, cdn);
|
this.item_code(doc, cdt, cdn);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -509,11 +509,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
show_batch_dialog = 1;
|
show_batch_dialog = 1;
|
||||||
}
|
}
|
||||||
// clear barcode if setting item (else barcode will take priority)
|
// clear barcode if setting item (else barcode will take priority)
|
||||||
if(!this.frm.from_barcode) {
|
if (this.frm.from_barcode == 0) {
|
||||||
item.barcode = null;
|
item.barcode = null;
|
||||||
}
|
}
|
||||||
|
this.frm.from_barcode = this.frm.from_barcode - 1 >= 0 ? this.frm.from_barcode - 1 : 0;
|
||||||
|
|
||||||
|
|
||||||
this.frm.from_barcode = false;
|
|
||||||
if(item.item_code || item.barcode || item.serial_no) {
|
if(item.item_code || item.barcode || item.serial_no) {
|
||||||
if(!this.validate_company_and_party()) {
|
if(!this.validate_company_and_party()) {
|
||||||
this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
|
this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
|
||||||
|
@ -192,19 +192,20 @@ class GSTR3BReport(Document):
|
|||||||
for d in self.report_dict["itc_elg"]["itc_avl"]:
|
for d in self.report_dict["itc_elg"]["itc_avl"]:
|
||||||
|
|
||||||
itc_type = itc_type_map.get(d["ty"])
|
itc_type = itc_type_map.get(d["ty"])
|
||||||
gst_category = ["Registered Regular"]
|
|
||||||
|
|
||||||
if d["ty"] == 'ISRC':
|
if d["ty"] == 'ISRC':
|
||||||
reverse_charge = "Y"
|
reverse_charge = ["Y"]
|
||||||
itc_type = 'All Other ITC'
|
itc_type = 'All Other ITC'
|
||||||
gst_category = ['Unregistered', 'Overseas']
|
gst_category = ['Unregistered', 'Overseas']
|
||||||
else:
|
else:
|
||||||
reverse_charge = "N"
|
gst_category = ['Unregistered', 'Overseas', 'Registered Regular']
|
||||||
|
reverse_charge = ["N", "Y"]
|
||||||
|
|
||||||
for account_head in self.account_heads:
|
for account_head in self.account_heads:
|
||||||
for category in gst_category:
|
for category in gst_category:
|
||||||
|
for charge_type in reverse_charge:
|
||||||
for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]:
|
for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]:
|
||||||
d[key[0]] += flt(itc_details.get((category, itc_type, reverse_charge, account_head.get(key[1])), {}).get("amount"), 2)
|
d[key[0]] += flt(itc_details.get((category, itc_type, charge_type, account_head.get(key[1])), {}).get("amount"), 2)
|
||||||
|
|
||||||
for key in ['iamt', 'camt', 'samt', 'csamt']:
|
for key in ['iamt', 'camt', 'samt', 'csamt']:
|
||||||
net_itc[key] += flt(d[key], 2)
|
net_itc[key] += flt(d[key], 2)
|
||||||
@ -264,7 +265,8 @@ class GSTR3BReport(Document):
|
|||||||
|
|
||||||
def get_itc_details(self):
|
def get_itc_details(self):
|
||||||
itc_amount = frappe.db.sql("""
|
itc_amount = frappe.db.sql("""
|
||||||
select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge
|
select s.gst_category, sum(t.base_tax_amount_after_discount_amount) as tax_amount,
|
||||||
|
t.account_head, s.eligibility_for_itc, s.reverse_charge
|
||||||
from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t
|
from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t
|
||||||
where s.docstatus = 1 and t.parent = s.name
|
where s.docstatus = 1 and t.parent = s.name
|
||||||
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
|
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
|
||||||
@ -387,7 +389,7 @@ class GSTR3BReport(Document):
|
|||||||
tax_template = 'Purchase Taxes and Charges'
|
tax_template = 'Purchase Taxes and Charges'
|
||||||
|
|
||||||
tax_amounts = frappe.db.sql("""
|
tax_amounts = frappe.db.sql("""
|
||||||
select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head
|
select s.gst_category, sum(t.base_tax_amount_after_discount_amount) as tax_amount, t.account_head
|
||||||
from `tab{doctype}` s , `tab{template}` t
|
from `tab{doctype}` s , `tab{template}` t
|
||||||
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
|
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
|
||||||
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
|
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
|
||||||
|
@ -12,6 +12,9 @@ erpnext.setup_auto_gst_taxation = (doctype) => {
|
|||||||
tax_category: function(frm) {
|
tax_category: function(frm) {
|
||||||
frm.trigger('get_tax_template');
|
frm.trigger('get_tax_template');
|
||||||
},
|
},
|
||||||
|
customer_address: function(frm) {
|
||||||
|
frm.trigger('get_tax_template');
|
||||||
|
},
|
||||||
get_tax_template: function(frm) {
|
get_tax_template: function(frm) {
|
||||||
if (!frm.doc.company) return;
|
if (!frm.doc.company) return;
|
||||||
|
|
||||||
@ -19,6 +22,7 @@ erpnext.setup_auto_gst_taxation = (doctype) => {
|
|||||||
'shipping_address': frm.doc.shipping_address || '',
|
'shipping_address': frm.doc.shipping_address || '',
|
||||||
'shipping_address_name': frm.doc.shipping_address_name || '',
|
'shipping_address_name': frm.doc.shipping_address_name || '',
|
||||||
'customer_address': frm.doc.customer_address || '',
|
'customer_address': frm.doc.customer_address || '',
|
||||||
|
'supplier_address': frm.doc.supplier_address,
|
||||||
'customer': frm.doc.customer,
|
'customer': frm.doc.customer,
|
||||||
'supplier': frm.doc.supplier,
|
'supplier': frm.doc.supplier,
|
||||||
'supplier_gstin': frm.doc.supplier_gstin,
|
'supplier_gstin': frm.doc.supplier_gstin,
|
||||||
|
@ -12,6 +12,7 @@ from erpnext.regional.india import number_state_mapping
|
|||||||
from six import string_types
|
from six import string_types
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
|
from frappe.model.utils import get_fetch_values
|
||||||
|
|
||||||
def validate_gstin_for_india(doc, method):
|
def validate_gstin_for_india(doc, method):
|
||||||
if hasattr(doc, 'gst_state') and doc.gst_state:
|
if hasattr(doc, 'gst_state') and doc.gst_state:
|
||||||
@ -161,6 +162,8 @@ def get_regional_address_details(party_details, doctype, company):
|
|||||||
party_details = json.loads(party_details)
|
party_details = json.loads(party_details)
|
||||||
party_details = frappe._dict(party_details)
|
party_details = frappe._dict(party_details)
|
||||||
|
|
||||||
|
update_party_details(party_details, doctype)
|
||||||
|
|
||||||
party_details.place_of_supply = get_place_of_supply(party_details, doctype)
|
party_details.place_of_supply = get_place_of_supply(party_details, doctype)
|
||||||
|
|
||||||
if is_internal_transfer(party_details, doctype):
|
if is_internal_transfer(party_details, doctype):
|
||||||
@ -209,6 +212,11 @@ def get_regional_address_details(party_details, doctype, company):
|
|||||||
|
|
||||||
return party_details
|
return party_details
|
||||||
|
|
||||||
|
def update_party_details(party_details, doctype):
|
||||||
|
for address_field in ['shipping_address', 'company_address', 'supplier_address', 'shipping_address_name', 'customer_address']:
|
||||||
|
if party_details.get(address_field):
|
||||||
|
party_details.update(get_fetch_values(doctype, address_field, party_details.get(address_field)))
|
||||||
|
|
||||||
def is_internal_transfer(party_details, doctype):
|
def is_internal_transfer(party_details, doctype):
|
||||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
||||||
destination_gstin = party_details.company_gstin
|
destination_gstin = party_details.company_gstin
|
||||||
|
@ -151,6 +151,7 @@ class Gstr1Report(object):
|
|||||||
{select_columns}
|
{select_columns}
|
||||||
from `tab{doctype}`
|
from `tab{doctype}`
|
||||||
where docstatus = 1 {where_conditions}
|
where docstatus = 1 {where_conditions}
|
||||||
|
and is_opening = 'No'
|
||||||
order by posting_date desc
|
order by posting_date desc
|
||||||
""".format(select_columns=self.select_columns, doctype=self.doctype,
|
""".format(select_columns=self.select_columns, doctype=self.doctype,
|
||||||
where_conditions=conditions), self.filters, as_dict=1)
|
where_conditions=conditions), self.filters, as_dict=1)
|
||||||
|
@ -58,6 +58,7 @@ class Customer(TransactionBase):
|
|||||||
self.set_loyalty_program()
|
self.set_loyalty_program()
|
||||||
self.check_customer_group_change()
|
self.check_customer_group_change()
|
||||||
self.validate_default_bank_account()
|
self.validate_default_bank_account()
|
||||||
|
self.validate_internal_customer()
|
||||||
|
|
||||||
# set loyalty program tier
|
# set loyalty program tier
|
||||||
if frappe.db.exists('Customer', self.name):
|
if frappe.db.exists('Customer', self.name):
|
||||||
@ -82,6 +83,11 @@ class Customer(TransactionBase):
|
|||||||
if not is_company_account:
|
if not is_company_account:
|
||||||
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
|
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
|
||||||
|
|
||||||
|
def validate_internal_customer(self):
|
||||||
|
if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"):
|
||||||
|
frappe.throw(_("Internal Customer for company {0} already exists").format(
|
||||||
|
frappe.bold(self.represents_company)))
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
self.validate_name_with_customer_group()
|
self.validate_name_with_customer_group()
|
||||||
self.create_primary_contact()
|
self.create_primary_contact()
|
||||||
@ -398,7 +404,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
|
|||||||
# form a list of emails and names to show to the user
|
# form a list of emails and names to show to the user
|
||||||
credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
|
credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
|
||||||
if not credit_controller_users_formatted:
|
if not credit_controller_users_formatted:
|
||||||
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
|
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.").format(customer))
|
||||||
|
|
||||||
message = """Please contact any of the following users to extend the credit limits for {0}:
|
message = """Please contact any of the following users to extend the credit limits for {0}:
|
||||||
<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users_formatted))
|
<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users_formatted))
|
||||||
|
@ -8,7 +8,7 @@ frappe.ui.form.on("Sales Order", {
|
|||||||
frm.custom_make_buttons = {
|
frm.custom_make_buttons = {
|
||||||
'Delivery Note': 'Delivery Note',
|
'Delivery Note': 'Delivery Note',
|
||||||
'Pick List': 'Pick List',
|
'Pick List': 'Pick List',
|
||||||
'Sales Invoice': 'Invoice',
|
'Sales Invoice': 'Sales Invoice',
|
||||||
'Material Request': 'Material Request',
|
'Material Request': 'Material Request',
|
||||||
'Purchase Order': 'Purchase Order',
|
'Purchase Order': 'Purchase Order',
|
||||||
'Project': 'Project',
|
'Project': 'Project',
|
||||||
|
@ -14,7 +14,6 @@ from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
|
|||||||
from frappe.desk.notifications import clear_doctype_notifications
|
from frappe.desk.notifications import clear_doctype_notifications
|
||||||
from frappe.contacts.doctype.address.address import get_company_address
|
from frappe.contacts.doctype.address.address import get_company_address
|
||||||
from erpnext.controllers.selling_controller import SellingController
|
from erpnext.controllers.selling_controller import SellingController
|
||||||
from frappe.automation.doctype.auto_repeat.auto_repeat import get_next_schedule_date
|
|
||||||
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
||||||
from erpnext.stock.doctype.item.item import get_item_defaults
|
from erpnext.stock.doctype.item.item import get_item_defaults
|
||||||
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
||||||
@ -418,8 +417,7 @@ class SalesOrder(SellingController):
|
|||||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||||
|
|
||||||
def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date):
|
def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date):
|
||||||
delivery_date = get_next_schedule_date(ref_doc_delivery_date,
|
delivery_date = auto_repeat_doc.get_next_schedule_date(schedule_date=ref_doc_delivery_date)
|
||||||
auto_repeat_doc.frequency, auto_repeat_doc.start_date, cint(auto_repeat_doc.repeat_on_day))
|
|
||||||
|
|
||||||
if delivery_date <= transaction_date:
|
if delivery_date <= transaction_date:
|
||||||
delivery_date_diff = frappe.utils.date_diff(ref_doc_delivery_date, red_doc_transaction_date)
|
delivery_date_diff = frappe.utils.date_diff(ref_doc_delivery_date, red_doc_transaction_date)
|
||||||
|
@ -111,24 +111,24 @@ erpnext.PointOfSale.Controller = class {
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare_app_defaults(data) {
|
async prepare_app_defaults(data) {
|
||||||
this.pos_opening = data.name;
|
this.pos_opening = data.name;
|
||||||
this.company = data.company;
|
this.company = data.company;
|
||||||
this.pos_profile = data.pos_profile;
|
this.pos_profile = data.pos_profile;
|
||||||
this.pos_opening_time = data.period_start_date;
|
this.pos_opening_time = data.period_start_date;
|
||||||
|
this.item_stock_map = {};
|
||||||
|
this.settings = {};
|
||||||
|
|
||||||
frappe.db.get_value('Stock Settings', undefined, 'allow_negative_stock').then(({ message }) => {
|
frappe.db.get_value('Stock Settings', undefined, 'allow_negative_stock').then(({ message }) => {
|
||||||
this.allow_negative_stock = flt(message.allow_negative_stock) || false;
|
this.allow_negative_stock = flt(message.allow_negative_stock) || false;
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
|
frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
|
||||||
this.customer_groups = profile.customer_groups.map(group => group.customer_group);
|
this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group);
|
||||||
this.cart.make_customer_selector();
|
this.settings.hide_images = profile.hide_images;
|
||||||
});
|
this.settings.auto_add_item_to_cart = profile.auto_add_item_to_cart;
|
||||||
|
|
||||||
this.item_stock_map = {};
|
|
||||||
|
|
||||||
this.make_app();
|
this.make_app();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
set_opening_entry_status() {
|
set_opening_entry_status() {
|
||||||
@ -238,12 +238,11 @@ erpnext.PointOfSale.Controller = class {
|
|||||||
this.item_selector = new erpnext.PointOfSale.ItemSelector({
|
this.item_selector = new erpnext.PointOfSale.ItemSelector({
|
||||||
wrapper: this.$components_wrapper,
|
wrapper: this.$components_wrapper,
|
||||||
pos_profile: this.pos_profile,
|
pos_profile: this.pos_profile,
|
||||||
|
settings: this.settings,
|
||||||
events: {
|
events: {
|
||||||
item_selected: args => this.on_cart_update(args),
|
item_selected: args => this.on_cart_update(args),
|
||||||
|
|
||||||
get_frm: () => this.frm || {},
|
get_frm: () => this.frm || {}
|
||||||
|
|
||||||
get_allowed_item_group: () => this.item_groups
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -251,6 +250,7 @@ erpnext.PointOfSale.Controller = class {
|
|||||||
init_item_cart() {
|
init_item_cart() {
|
||||||
this.cart = new erpnext.PointOfSale.ItemCart({
|
this.cart = new erpnext.PointOfSale.ItemCart({
|
||||||
wrapper: this.$components_wrapper,
|
wrapper: this.$components_wrapper,
|
||||||
|
settings: this.settings,
|
||||||
events: {
|
events: {
|
||||||
get_frm: () => this.frm,
|
get_frm: () => this.frm,
|
||||||
|
|
||||||
@ -273,9 +273,7 @@ erpnext.PointOfSale.Controller = class {
|
|||||||
this.customer_details = details;
|
this.customer_details = details;
|
||||||
// will add/remove LP payment method
|
// will add/remove LP payment method
|
||||||
this.payment.render_loyalty_points_payment_mode();
|
this.payment.render_loyalty_points_payment_mode();
|
||||||
},
|
}
|
||||||
|
|
||||||
get_allowed_customer_group: () => this.customer_groups
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
erpnext.PointOfSale.ItemCart = class {
|
erpnext.PointOfSale.ItemCart = class {
|
||||||
constructor({ wrapper, events }) {
|
constructor({ wrapper, events, settings }) {
|
||||||
this.wrapper = wrapper;
|
this.wrapper = wrapper;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.customer_info = undefined;
|
this.customer_info = undefined;
|
||||||
|
this.hide_images = settings.hide_images;
|
||||||
|
this.allowed_customer_groups = settings.customer_groups;
|
||||||
|
|
||||||
this.init_component();
|
this.init_component();
|
||||||
}
|
}
|
||||||
@ -32,6 +34,7 @@ erpnext.PointOfSale.ItemCart = class {
|
|||||||
`<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
|
`<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
|
||||||
)
|
)
|
||||||
this.$customer_section = this.$component.find('.customer-section');
|
this.$customer_section = this.$component.find('.customer-section');
|
||||||
|
this.make_customer_selector();
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_customer_selector() {
|
reset_customer_selector() {
|
||||||
@ -302,7 +305,7 @@ erpnext.PointOfSale.ItemCart = class {
|
|||||||
this.$customer_section.html(`<div class="customer-search-field flex flex-1 items-center"></div>`);
|
this.$customer_section.html(`<div class="customer-search-field flex flex-1 items-center"></div>`);
|
||||||
const me = this;
|
const me = this;
|
||||||
const query = { query: 'erpnext.controllers.queries.customer_query' };
|
const query = { query: 'erpnext.controllers.queries.customer_query' };
|
||||||
const allowed_customer_group = this.events.get_allowed_customer_group() || [];
|
const allowed_customer_group = this.allowed_customer_groups || [];
|
||||||
if (allowed_customer_group.length) {
|
if (allowed_customer_group.length) {
|
||||||
query.filters = {
|
query.filters = {
|
||||||
customer_group: ['in', allowed_customer_group]
|
customer_group: ['in', allowed_customer_group]
|
||||||
@ -423,6 +426,7 @@ erpnext.PointOfSale.ItemCart = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_customer_section() {
|
update_customer_section() {
|
||||||
|
const me = this;
|
||||||
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
|
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
|
||||||
|
|
||||||
if (customer) {
|
if (customer) {
|
||||||
@ -460,7 +464,7 @@ erpnext.PointOfSale.ItemCart = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function get_customer_image() {
|
function get_customer_image() {
|
||||||
if (image) {
|
if (!me.hide_images && image) {
|
||||||
return `<div class="icon flex items-center justify-center w-12 h-12 rounded bg-light-grey mr-4 text-grey-200">
|
return `<div class="icon flex items-center justify-center w-12 h-12 rounded bg-light-grey mr-4 text-grey-200">
|
||||||
<img class="h-full" src="${image}" alt="${image}" style="object-fit: cover;">
|
<img class="h-full" src="${image}" alt="${image}" style="object-fit: cover;">
|
||||||
</div>`
|
</div>`
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
erpnext.PointOfSale.ItemSelector = class {
|
erpnext.PointOfSale.ItemSelector = class {
|
||||||
constructor({ frm, wrapper, events, pos_profile }) {
|
constructor({ frm, wrapper, events, pos_profile, settings }) {
|
||||||
this.wrapper = wrapper;
|
this.wrapper = wrapper;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
this.pos_profile = pos_profile;
|
this.pos_profile = pos_profile;
|
||||||
|
this.hide_images = settings.hide_images;
|
||||||
|
this.auto_add_item = settings.auto_add_item_to_cart;
|
||||||
|
|
||||||
this.inti_component();
|
this.inti_component();
|
||||||
}
|
}
|
||||||
@ -33,6 +35,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.$component = this.wrapper.find('.items-selector');
|
this.$component = this.wrapper.find('.items-selector');
|
||||||
|
this.$items_container = this.$component.find('.items-container');
|
||||||
}
|
}
|
||||||
|
|
||||||
async load_items_data() {
|
async load_items_data() {
|
||||||
@ -65,7 +68,6 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
|
|
||||||
|
|
||||||
render_item_list(items) {
|
render_item_list(items) {
|
||||||
this.$items_container = this.$component.find('.items-container');
|
|
||||||
this.$items_container.html('');
|
this.$items_container.html('');
|
||||||
|
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
@ -75,11 +77,12 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_item_html(item) {
|
get_item_html(item) {
|
||||||
|
const me = this;
|
||||||
const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
|
const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
|
||||||
const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange";
|
const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange";
|
||||||
|
|
||||||
function get_item_image_html() {
|
function get_item_image_html() {
|
||||||
if (item_image) {
|
if (!me.hide_images && item_image) {
|
||||||
return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
|
return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
|
||||||
<img class="h-full" src="${item_image}" alt="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
|
<img class="h-full" src="${item_image}" alt="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
|
||||||
</div>`
|
</div>`
|
||||||
@ -203,6 +206,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
ignore_inputs: true,
|
ignore_inputs: true,
|
||||||
page: cur_page.page.page
|
page: cur_page.page.page
|
||||||
});
|
});
|
||||||
|
|
||||||
// for selecting the last filtered item on search
|
// for selecting the last filtered item on search
|
||||||
frappe.ui.keys.on("enter", () => {
|
frappe.ui.keys.on("enter", () => {
|
||||||
const selector_is_visible = this.$component.is(':visible');
|
const selector_is_visible = this.$component.is(':visible');
|
||||||
@ -235,6 +239,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
const items = this.search_index[search_term];
|
const items = this.search_index[search_term];
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.render_item_list(items);
|
this.render_item_list(items);
|
||||||
|
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,9 +252,14 @@ erpnext.PointOfSale.ItemSelector = class {
|
|||||||
}
|
}
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.render_item_list(items);
|
this.render_item_list(items);
|
||||||
|
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_filtered_item_to_cart() {
|
||||||
|
this.$items_container.find(".item-wrapper").click();
|
||||||
|
}
|
||||||
|
|
||||||
resize_selector(minimize) {
|
resize_selector(minimize) {
|
||||||
minimize ?
|
minimize ?
|
||||||
this.$component.find('.search-field').removeClass('mr-8') :
|
this.$component.find('.search-field').removeClass('mr-8') :
|
||||||
|
@ -10,7 +10,7 @@ from frappe.utils.nestedset import get_descendants_of
|
|||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
filters = frappe._dict(filters or {})
|
filters = frappe._dict(filters or {})
|
||||||
if filters.from_date > filters.to_date:
|
if filters.from_date > filters.to_date:
|
||||||
frappe.throw(_('From Date cannot be greater than To Date'))
|
frappe.throw(_("From Date cannot be greater than To Date"))
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
data = get_data(filters)
|
data = get_data(filters)
|
||||||
@ -148,14 +148,16 @@ def get_data(filters):
|
|||||||
company_list.append(filters.get("company"))
|
company_list.append(filters.get("company"))
|
||||||
|
|
||||||
customer_details = get_customer_details()
|
customer_details = get_customer_details()
|
||||||
|
item_details = get_item_details()
|
||||||
sales_order_records = get_sales_order_details(company_list, filters)
|
sales_order_records = get_sales_order_details(company_list, filters)
|
||||||
|
|
||||||
for record in sales_order_records:
|
for record in sales_order_records:
|
||||||
customer_record = customer_details.get(record.customer)
|
customer_record = customer_details.get(record.customer)
|
||||||
|
item_record = item_details.get(record.item_code)
|
||||||
row = {
|
row = {
|
||||||
"item_code": record.item_code,
|
"item_code": record.item_code,
|
||||||
"item_name": record.item_name,
|
"item_name": item_record.item_name,
|
||||||
"item_group": record.item_group,
|
"item_group": item_record.item_group,
|
||||||
"description": record.description,
|
"description": record.description,
|
||||||
"quantity": record.qty,
|
"quantity": record.qty,
|
||||||
"uom": record.uom,
|
"uom": record.uom,
|
||||||
@ -196,8 +198,8 @@ def get_conditions(filters):
|
|||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_customer_details():
|
def get_customer_details():
|
||||||
details = frappe.get_all('Customer',
|
details = frappe.get_all("Customer",
|
||||||
fields=['name', 'customer_name', "customer_group"])
|
fields=["name", "customer_name", "customer_group"])
|
||||||
customer_details = {}
|
customer_details = {}
|
||||||
for d in details:
|
for d in details:
|
||||||
customer_details.setdefault(d.name, frappe._dict({
|
customer_details.setdefault(d.name, frappe._dict({
|
||||||
@ -206,15 +208,25 @@ def get_customer_details():
|
|||||||
}))
|
}))
|
||||||
return customer_details
|
return customer_details
|
||||||
|
|
||||||
|
def get_item_details():
|
||||||
|
details = frappe.db.get_all("Item",
|
||||||
|
fields=["item_code", "item_name", "item_group"])
|
||||||
|
item_details = {}
|
||||||
|
for d in details:
|
||||||
|
item_details.setdefault(d.item_code, frappe._dict({
|
||||||
|
"item_name": d.item_name,
|
||||||
|
"item_group": d.item_group
|
||||||
|
}))
|
||||||
|
return item_details
|
||||||
|
|
||||||
def get_sales_order_details(company_list, filters):
|
def get_sales_order_details(company_list, filters):
|
||||||
conditions = get_conditions(filters)
|
conditions = get_conditions(filters)
|
||||||
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
so_item.item_code, so_item.item_name, so_item.item_group,
|
so_item.item_code, so_item.description, so_item.qty,
|
||||||
so_item.description, so_item.qty, so_item.uom,
|
so_item.uom, so_item.base_rate, so_item.base_amount,
|
||||||
so_item.base_rate, so_item.base_amount, so.name,
|
so.name, so.transaction_date, so.customer,so.territory,
|
||||||
so.transaction_date, so.customer, so.territory,
|
|
||||||
so.project, so_item.delivered_qty,
|
so.project, so_item.delivered_qty,
|
||||||
so_item.billed_amt, so.company
|
so_item.billed_amt, so.company
|
||||||
FROM
|
FROM
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cards": [
|
"cards": [
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"label": "Healthcare",
|
|
||||||
"links": "[\n {\n \"label\": \"Patient\",\n \"name\": \"Patient\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Diagnosis\",\n \"name\": \"Diagnosis\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"label": "Agriculture",
|
|
||||||
"links": "[\n {\n \"label\": \"Crop\",\n \"name\": \"Crop\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Crop Cycle\",\n \"name\": \"Crop Cycle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Location\",\n \"name\": \"Location\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Fertilizer\",\n \"name\": \"Fertilizer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"label": "Education",
|
|
||||||
"links": "[\n {\n \"label\": \"Student\",\n \"name\": \"Student\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Course\",\n \"name\": \"Course\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Instructor\",\n \"name\": \"Instructor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Room\",\n \"name\": \"Room\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"label": "Non Profit",
|
|
||||||
"links": "[\n {\n \"description\": \"Member information.\",\n \"label\": \"Member\",\n \"name\": \"Member\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Volunteer information.\",\n \"label\": \"Volunteer\",\n \"name\": \"Volunteer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Chapter information.\",\n \"label\": \"Chapter\",\n \"name\": \"Chapter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Donor information.\",\n \"label\": \"Donor\",\n \"name\": \"Donor\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Stock",
|
"label": "Stock",
|
||||||
@ -54,10 +34,11 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Desk Page",
|
"doctype": "Desk Page",
|
||||||
"extends_another_page": 0,
|
"extends_another_page": 0,
|
||||||
|
"hide_custom": 0,
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Home",
|
"label": "Home",
|
||||||
"modified": "2020-05-11 10:20:37.358701",
|
"modified": "2020-12-07 14:22:38.667767",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Home",
|
"name": "Home",
|
||||||
|
@ -274,7 +274,8 @@ erpnext.company.setup_queries = function(frm) {
|
|||||||
["default_employee_advance_account", {"root_type": "Asset"}],
|
["default_employee_advance_account", {"root_type": "Asset"}],
|
||||||
["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
|
["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
|
||||||
["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
|
["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
|
||||||
["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}]
|
["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
|
||||||
|
["unrealized_profit_loss_account", {"root_type": "Liability"}]
|
||||||
], function(i, v) {
|
], function(i, v) {
|
||||||
erpnext.company.set_custom_query(frm, v);
|
erpnext.company.set_custom_query(frm, v);
|
||||||
});
|
});
|
||||||
|
@ -46,10 +46,9 @@
|
|||||||
"round_off_account",
|
"round_off_account",
|
||||||
"round_off_cost_center",
|
"round_off_cost_center",
|
||||||
"write_off_account",
|
"write_off_account",
|
||||||
"discount_allowed_account",
|
|
||||||
"discount_received_account",
|
|
||||||
"exchange_gain_loss_account",
|
"exchange_gain_loss_account",
|
||||||
"unrealized_exchange_gain_loss_account",
|
"unrealized_exchange_gain_loss_account",
|
||||||
|
"unrealized_profit_loss_account",
|
||||||
"column_break0",
|
"column_break0",
|
||||||
"allow_account_creation_against_child_company",
|
"allow_account_creation_against_child_company",
|
||||||
"default_payable_account",
|
"default_payable_account",
|
||||||
@ -261,14 +260,14 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "create_chart_of_accounts_based_on",
|
"fieldname": "create_chart_of_accounts_based_on",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Create Chart of Accounts Based on",
|
"label": "Create Chart Of Accounts Based On",
|
||||||
"options": "\nStandard Template\nExisting Company"
|
"options": "\nStandard Template\nExisting Company"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"",
|
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"",
|
||||||
"fieldname": "chart_of_accounts",
|
"fieldname": "chart_of_accounts",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Chart of Accounts Template",
|
"label": "Chart Of Accounts Template",
|
||||||
"no_copy": 1
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -345,18 +344,6 @@
|
|||||||
"label": "Write Off Account",
|
"label": "Write Off Account",
|
||||||
"options": "Account"
|
"options": "Account"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "discount_allowed_account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Discount Allowed Account",
|
|
||||||
"options": "Account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "discount_received_account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Discount Received Account",
|
|
||||||
"options": "Account"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "exchange_gain_loss_account",
|
"fieldname": "exchange_gain_loss_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -740,6 +727,12 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Default In Transit Warehouse",
|
"label": "Default In Transit Warehouse",
|
||||||
"options": "Warehouse"
|
"options": "Warehouse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "unrealized_profit_loss_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Unrealized Profit / Loss Account",
|
||||||
|
"options": "Account"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-building",
|
"icon": "fa fa-building",
|
||||||
@ -747,7 +740,7 @@
|
|||||||
"image_field": "company_logo",
|
"image_field": "company_logo",
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-08-06 00:38:08.311216",
|
"modified": "2020-12-03 12:27:27.085094",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Company",
|
"name": "Company",
|
||||||
|
@ -133,6 +133,7 @@
|
|||||||
"per_installed",
|
"per_installed",
|
||||||
"installation_status",
|
"installation_status",
|
||||||
"column_break_89",
|
"column_break_89",
|
||||||
|
"per_returned",
|
||||||
"excise_page",
|
"excise_page",
|
||||||
"instructions",
|
"instructions",
|
||||||
"subscription_section",
|
"subscription_section",
|
||||||
@ -1099,7 +1100,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed",
|
"options": "\nDraft\nTo Bill\nCompleted\nReturn Issued\nCancelled\nClosed",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
@ -1251,13 +1252,22 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Inter Company Reference",
|
"label": "Inter Company Reference",
|
||||||
"options": "Purchase Receipt"
|
"options": "Purchase Receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:!doc.__islocal",
|
||||||
|
"fieldname": "per_returned",
|
||||||
|
"fieldtype": "Percent",
|
||||||
|
"label": "% Returned",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-truck",
|
"icon": "fa fa-truck",
|
||||||
"idx": 146,
|
"idx": 146,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-11 14:57:16.388139",
|
"modified": "2020-11-30 12:54:45.407289",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note",
|
"name": "Delivery Note",
|
||||||
|
@ -55,7 +55,7 @@ class DeliveryNote(SellingController):
|
|||||||
'no_allowance': 1
|
'no_allowance': 1
|
||||||
}]
|
}]
|
||||||
if cint(self.is_return):
|
if cint(self.is_return):
|
||||||
self.status_updater.append({
|
self.status_updater.extend([{
|
||||||
'source_dt': 'Delivery Note Item',
|
'source_dt': 'Delivery Note Item',
|
||||||
'target_dt': 'Sales Order Item',
|
'target_dt': 'Sales Order Item',
|
||||||
'join_field': 'so_detail',
|
'join_field': 'so_detail',
|
||||||
@ -69,7 +69,19 @@ class DeliveryNote(SellingController):
|
|||||||
where name=`tabDelivery Note Item`.parent and is_return=1)""",
|
where name=`tabDelivery Note Item`.parent and is_return=1)""",
|
||||||
'second_source_extra_cond': """ and exists (select name from `tabSales Invoice`
|
'second_source_extra_cond': """ and exists (select name from `tabSales Invoice`
|
||||||
where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
'source_dt': 'Delivery Note Item',
|
||||||
|
'target_dt': 'Delivery Note Item',
|
||||||
|
'join_field': 'dn_detail',
|
||||||
|
'target_field': 'returned_qty',
|
||||||
|
'target_parent_dt': 'Delivery Note',
|
||||||
|
'target_parent_field': 'per_returned',
|
||||||
|
'target_ref_field': 'stock_qty',
|
||||||
|
'source_field': '-1 * stock_qty',
|
||||||
|
'percent_join_field_parent': 'return_against'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
def before_print(self):
|
def before_print(self):
|
||||||
def toggle_print_hide(meta, fieldname):
|
def toggle_print_hide(meta, fieldname):
|
||||||
|
@ -6,9 +6,11 @@ frappe.listview_settings['Delivery Note'] = {
|
|||||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||||
} else if (doc.status === "Closed") {
|
} else if (doc.status === "Closed") {
|
||||||
return [__("Closed"), "green", "status,=,Closed"];
|
return [__("Closed"), "green", "status,=,Closed"];
|
||||||
|
} else if (flt(doc.per_returned, 2) === 100) {
|
||||||
|
return [__("Return Issued"), "grey", "per_returned,=,100"];
|
||||||
} else if (flt(doc.per_billed, 2) < 100) {
|
} else if (flt(doc.per_billed, 2) < 100) {
|
||||||
return [__("To Bill"), "orange", "per_billed,<,100"];
|
return [__("To Bill"), "orange", "per_billed,<,100"];
|
||||||
} else if (flt(doc.per_billed, 2) == 100) {
|
} else if (flt(doc.per_billed, 2) === 100) {
|
||||||
return [__("Completed"), "green", "per_billed,=,100"];
|
return [__("Completed"), "green", "per_billed,=,100"];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -206,7 +206,7 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
for field, value in field_values.items():
|
for field, value in field_values.items():
|
||||||
self.assertEqual(cstr(serial_no.get(field)), value)
|
self.assertEqual(cstr(serial_no.get(field)), value)
|
||||||
|
|
||||||
def test_sales_return_for_non_bundled_items(self):
|
def test_sales_return_for_non_bundled_items_partial(self):
|
||||||
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
|
make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
|
||||||
@ -225,7 +225,10 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
|
|
||||||
# return entry
|
# return entry
|
||||||
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500,
|
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500,
|
||||||
company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
|
company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1",
|
||||||
|
cost_center="Main - TCP1", do_not_submit=1)
|
||||||
|
dn1.items[0].dn_detail = dn.items[0].name
|
||||||
|
dn1.submit()
|
||||||
|
|
||||||
actual_qty_2 = get_qty_after_transaction(warehouse="Stores - TCP1")
|
actual_qty_2 = get_qty_after_transaction(warehouse="Stores - TCP1")
|
||||||
|
|
||||||
@ -243,6 +246,70 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(gle_warehouse_amount, stock_value_difference)
|
self.assertEqual(gle_warehouse_amount, stock_value_difference)
|
||||||
|
|
||||||
|
# hack because new_doc isn't considering is_return portion of status_updater
|
||||||
|
returned = frappe.get_doc("Delivery Note", dn1.name)
|
||||||
|
returned.update_prevdoc_status()
|
||||||
|
dn.load_from_db()
|
||||||
|
|
||||||
|
# Check if Original DN updated
|
||||||
|
self.assertEqual(dn.items[0].returned_qty, 2)
|
||||||
|
self.assertEqual(dn.per_returned, 40)
|
||||||
|
|
||||||
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
|
return_dn_2 = make_return_doc("Delivery Note", dn.name)
|
||||||
|
|
||||||
|
# Check if unreturned amount is mapped in 2nd return
|
||||||
|
self.assertEqual(return_dn_2.items[0].qty, -3)
|
||||||
|
|
||||||
|
si = make_sales_invoice(dn.name)
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
self.assertEqual(si.items[0].qty, 3)
|
||||||
|
|
||||||
|
dn.load_from_db()
|
||||||
|
# DN should be completed on billing all unreturned amount
|
||||||
|
self.assertEqual(dn.items[0].billed_amt, 1500)
|
||||||
|
self.assertEqual(dn.per_billed, 100)
|
||||||
|
self.assertEqual(dn.status, 'Completed')
|
||||||
|
|
||||||
|
si.load_from_db()
|
||||||
|
si.cancel()
|
||||||
|
|
||||||
|
dn.load_from_db()
|
||||||
|
self.assertEqual(dn.per_billed, 0)
|
||||||
|
|
||||||
|
dn1.cancel()
|
||||||
|
dn.cancel()
|
||||||
|
|
||||||
|
def test_sales_return_for_non_bundled_items_full(self):
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
|
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
||||||
|
|
||||||
|
make_item("Box", {'is_stock_item': 1})
|
||||||
|
|
||||||
|
make_stock_entry(item_code="Box", target="Stores - TCP1", qty=10, basic_rate=100)
|
||||||
|
|
||||||
|
dn = create_delivery_note(item_code="Box", qty=5, rate=500, warehouse="Stores - TCP1", company=company,
|
||||||
|
expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
|
||||||
|
|
||||||
|
#return entry
|
||||||
|
dn1 = create_delivery_note(item_code="Box", is_return=1, return_against=dn.name, qty=-5, rate=500,
|
||||||
|
company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1",
|
||||||
|
cost_center="Main - TCP1", do_not_submit=1)
|
||||||
|
dn1.items[0].dn_detail = dn.items[0].name
|
||||||
|
dn1.submit()
|
||||||
|
|
||||||
|
# hack because new_doc isn't considering is_return portion of status_updater
|
||||||
|
returned = frappe.get_doc("Delivery Note", dn1.name)
|
||||||
|
returned.update_prevdoc_status()
|
||||||
|
dn.load_from_db()
|
||||||
|
|
||||||
|
# Check if Original DN updated
|
||||||
|
self.assertEqual(dn.items[0].returned_qty, 5)
|
||||||
|
self.assertEqual(dn.per_returned, 100)
|
||||||
|
self.assertEqual(dn.status, 'Return Issued')
|
||||||
|
|
||||||
def test_return_single_item_from_bundled_items(self):
|
def test_return_single_item_from_bundled_items(self):
|
||||||
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user