diff --git a/.gitignore b/.gitignore index d641ced0a8..68272c7d4d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ dist/ erpnext/docs/current *.swp *.swo +__pycache__ *~ diff --git a/erpnext/__init__.py b/erpnext/__init__.py index fd0f5c1936..99981bd713 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '10.1.4' +__version__ = '10.1.6' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 4f77a452a3..76d5f95a3c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -145,13 +145,13 @@ frappe.ui.form.on('Payment Entry', { frm.doc.paid_amount : frm.doc.received_amount; frm.toggle_display("write_off_difference_amount", (frm.doc.difference_amount && frm.doc.party && - (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) && (frm.doc.total_allocated_amount > party_amount))); frm.toggle_display("set_exchange_gain_loss", (frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount && - (frm.doc.paid_from_account_currency != company_currency || - frm.doc.paid_to_account_currency != company_currency))); + ((frm.doc.paid_from_account_currency != company_currency || + frm.doc.paid_to_account_currency != company_currency) && + frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency))); frm.refresh_fields(); }, @@ -300,7 +300,15 @@ frappe.ui.form.on('Payment Entry', { if(frm.doc.payment_type == "Pay") { frm.events.get_outstanding_documents(frm); } else if (frm.doc.payment_type == "Receive") { - frm.events.received_amount(frm); + if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { + if(frm.doc.source_exchange_rate) { + frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); + } + frm.set_value("received_amount", frm.doc.paid_amount); + + } else { + frm.events.received_amount(frm); + } } } ); @@ -317,26 +325,31 @@ frappe.ui.form.on('Payment Entry', { }, callback: function(r, rt) { if(r.message) { - frm.set_value(currency_field, r.message['account_currency']); - frm.set_value(balance_field, r.message['account_balance']); + frappe.run_serially([ + () => frm.set_value(currency_field, r.message['account_currency']), + () => { + frm.set_value(balance_field, r.message['account_balance']); - if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") { - frm.toggle_reqd(["reference_no", "reference_date"], - (r.message['account_type'] == "Bank" ? 1 : 0)); - if(!frm.doc.received_amount && frm.doc.paid_amount) - frm.events.paid_amount(frm); - } else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") { - frm.toggle_reqd(["reference_no", "reference_date"], - (r.message['account_type'] == "Bank" ? 1 : 0)); + if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") { + frm.toggle_reqd(["reference_no", "reference_date"], + (r.message['account_type'] == "Bank" ? 1 : 0)); + if(!frm.doc.received_amount && frm.doc.paid_amount) + frm.events.paid_amount(frm); + } else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") { + frm.toggle_reqd(["reference_no", "reference_date"], + (r.message['account_type'] == "Bank" ? 1 : 0)); - if(!frm.doc.paid_amount && frm.doc.received_amount) - frm.events.received_amount(frm); - } + if(!frm.doc.paid_amount && frm.doc.received_amount) + frm.events.received_amount(frm); + } + }, + () => { + if(callback_function) callback_function(frm); - if(callback_function) callback_function(frm); - - frm.events.hide_unhide_fields(frm); - frm.events.set_dynamic_labels(frm); + frm.events.hide_unhide_fields(frm); + frm.events.set_dynamic_labels(frm); + } + ]); } } }); @@ -405,7 +418,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_value("base_received_amount", frm.doc.base_paid_amount); } - frm.events.set_difference_amount(frm); + frm.events.set_unallocated_amount(frm); } // Make read only if Accounts Settings doesn't allow stale rates @@ -425,7 +438,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_value("base_paid_amount", frm.doc.base_received_amount); } - frm.events.set_difference_amount(frm); + frm.events.set_unallocated_amount(frm); } frm.set_paid_amount_based_on_received_amount = false; @@ -456,7 +469,7 @@ frappe.ui.form.on('Payment Entry', { if(frm.doc.payment_type == "Pay") frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount); else - frm.events.set_difference_amount(frm); + frm.events.set_unallocated_amount(frm); frm.set_paid_amount_based_on_received_amount = false; }, @@ -476,7 +489,7 @@ frappe.ui.form.on('Payment Entry', { if(frm.doc.payment_type == "Receive") frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount); else - frm.events.set_difference_amount(frm); + frm.events.set_unallocated_amount(frm); }, get_outstanding_documents: function(frm) { @@ -565,8 +578,11 @@ frappe.ui.form.on('Payment Entry', { if(frm.doc.references.length == 0){ frm.events.get_outstanding_documents(frm); } - - frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount); + if(frm.doc.payment_type == 'Internal Transfer') { + frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount); + } else { + frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount); + } }, allocate_party_amount_against_ref_docs: function(frm, paid_amount) { @@ -651,10 +667,10 @@ frappe.ui.form.on('Payment Entry', { frm.set_value("total_allocated_amount", Math.abs(total_allocated_amount)); frm.set_value("base_total_allocated_amount", Math.abs(base_total_allocated_amount)); - frm.events.set_difference_amount(frm); + frm.events.set_unallocated_amount(frm); }, - set_difference_amount: function(frm) { + set_unallocated_amount: function(frm) { var unallocated_amount = 0; var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [], function(d) { return flt(d.amount) })); @@ -662,17 +678,34 @@ frappe.ui.form.on('Payment Entry', { if(frm.doc.party) { var party_amount = frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount; + var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: ""; - if(frm.doc.total_allocated_amount < party_amount) { - if(frm.doc.payment_type == "Receive") { + if (frm.doc.party_account_currency == company_currency) { + if(frm.doc.payment_type == "Receive" && frm.doc.total_allocated_amount <= party_amount + total_deductions) { unallocated_amount = party_amount - (frm.doc.total_allocated_amount - total_deductions); - } else { + } else if (frm.doc.payment_type == "Pay" && frm.doc.total_allocated_amount <= party_amount - total_deductions) { unallocated_amount = party_amount - (frm.doc.total_allocated_amount + total_deductions); } + } else { + if(frm.doc.payment_type == "Receive" + && frm.doc.base_total_allocated_amount <= frm.doc.base_received_amount + total_deductions + && frm.doc.total_allocated_amount < frm.doc.paid_amount) { + unallocated_amount = (frm.doc.base_received_amount + total_deductions + - frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate; + } else if (frm.doc.payment_type == "Pay" + && frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions + && frm.doc.total_allocated_amount < frm.doc.received_amount) { + unallocated_amount = (frm.doc.base_paid_amount - (total_deductions + + frm.doc.base_total_allocated_amount)) / frm.doc.target_exchange_rate; + } } + } frm.set_value("unallocated_amount", unallocated_amount); - + frm.trigger("set_difference_amount"); + }, + + set_difference_amount: function(frm) { var difference_amount = 0; var base_unallocated_amount = flt(frm.doc.unallocated_amount) * (frm.doc.payment_type=="Receive" ? frm.doc.source_exchange_rate : frm.doc.target_exchange_rate); @@ -687,11 +720,18 @@ frappe.ui.form.on('Payment Entry', { difference_amount = flt(frm.doc.base_paid_amount) - flt(frm.doc.base_received_amount); } + var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [], + function(d) { return flt(d.amount) })); + frm.set_value("difference_amount", difference_amount - total_deductions); frm.events.hide_unhide_fields(frm); }, + unallocated_amount: function(frm) { + frm.trigger("set_difference_amount"); + }, + check_mandatory_to_fetch: function(frm) { $.each(["Company", "Party Type", "Party", "payment_type"], function(i, field) { if(!frm.doc[frappe.model.scrub(field)]) { @@ -771,7 +811,7 @@ frappe.ui.form.on('Payment Entry', { row.amount = flt(row.amount) + flt(frm.doc.difference_amount); refresh_field("deductions"); - frm.events.set_difference_amount(frm); + frm.events.set_unallocated_amount(frm); } } }) @@ -818,10 +858,10 @@ frappe.ui.form.on('Payment Entry Reference', { frappe.ui.form.on('Payment Entry Deduction', { amount: function(frm) { - frm.events.set_difference_amount(frm); + frm.events.set_unallocated_amount(frm); }, deductions_remove: function(frm) { - frm.events.set_difference_amount(frm); + frm.events.set_unallocated_amount(frm); } }) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index fb0a612474..60a54c22ba 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -40,6 +40,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -71,6 +72,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 1, + "translatable": 0, "unique": 0 }, { @@ -102,6 +104,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -131,6 +134,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -162,6 +166,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -193,6 +198,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -224,6 +230,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -255,6 +262,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -288,6 +296,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -320,6 +329,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -349,6 +359,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -381,6 +392,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -411,6 +423,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -442,6 +455,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -474,6 +488,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -506,6 +521,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -538,6 +554,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -567,6 +584,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -599,6 +617,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -631,6 +650,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -663,6 +683,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -695,6 +716,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -727,6 +749,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -758,6 +781,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -790,6 +814,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -819,6 +844,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -851,6 +877,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -882,6 +909,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -914,6 +942,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -946,6 +975,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -978,6 +1008,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1010,6 +1041,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1041,6 +1073,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1072,6 +1105,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1104,6 +1138,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1134,6 +1169,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1163,6 +1199,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1188,12 +1225,13 @@ "precision": "", "print_hide": 1, "print_hide_if_no_value": 0, - "read_only": 1, + "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1226,6 +1264,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1257,6 +1296,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1289,6 +1329,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1320,6 +1361,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1350,6 +1392,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1381,6 +1424,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1410,6 +1454,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1441,6 +1486,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1472,6 +1518,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1504,6 +1551,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1536,6 +1584,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1566,6 +1615,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1595,6 +1645,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1626,6 +1677,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1657,6 +1709,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1687,6 +1740,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1718,6 +1772,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1748,6 +1803,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1778,6 +1834,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -1791,7 +1848,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-01-11 14:43:03.336327", + "modified": "2018-02-19 16:58:23.899015", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 3f7c9d9b6c..8800a0a7e4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -21,6 +21,11 @@ class InvalidPaymentEntry(ValidationError): class PaymentEntry(AccountsController): + def __init__(self, *args, **kwargs): + super(PaymentEntry, self).__init__(*args, **kwargs) + if not self.is_new(): + self.setup_party_account_field() + def setup_party_account_field(self): self.party_account_field = None self.party_account = None @@ -287,17 +292,31 @@ class PaymentEntry(AccountsController): self.base_total_allocated_amount = abs(base_total_allocated_amount) def set_unallocated_amount(self): - self.unallocated_amount = 0; - if self.party: - party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount + self.unallocated_amount = 0 + if self.party: total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) - if self.total_allocated_amount < party_amount: - if self.payment_type == "Receive": - self.unallocated_amount = party_amount - (self.total_allocated_amount - total_deductions) - else: - self.unallocated_amount = party_amount - (self.total_allocated_amount + total_deductions) + if self.party_account_currency == self.company_currency: + if self.payment_type == "Receive" \ + and self.total_allocated_amount <= self.paid_amount + total_deductions: + self.unallocated_amount = self.paid_amount - \ + (self.total_allocated_amount - total_deductions) + elif self.payment_type == "Pay" \ + and self.total_allocated_amount <= self.received_amount - total_deductions: + self.unallocated_amount = self.received_amount - \ + (self.total_allocated_amount + total_deductions) + else: + if self.payment_type == "Receive" \ + and self.base_total_allocated_amount <= self.base_received_amount + total_deductions \ + and self.total_allocated_amount < self.paid_amount: + self.unallocated_amount = (self.base_received_amount + total_deductions - + self.base_total_allocated_amount) / self.source_exchange_rate + elif self.payment_type == "Pay" \ + and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ + and self.total_allocated_amount < self.received_amount: + self.unallocated_amount = (self.base_paid_amount - (total_deductions + + self.base_total_allocated_amount)) / self.target_exchange_rate def set_difference_amount(self): base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index f4b813a009..435bee0659 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -141,7 +141,6 @@ class TestPaymentEntry(unittest.TestCase): def test_payment_entry_retrieves_last_exchange_rate(self): from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records - test_records = test_records save_new_records(test_records) pe = frappe.new_doc("Payment Entry") @@ -151,6 +150,7 @@ class TestPaymentEntry(unittest.TestCase): pe.paid_from = "_Test Bank USD - _TC" pe.paid_to = "_Test Bank - _TC" pe.paid_amount = 100 + pe.received_amount = 100 pe.reference_no = "3" pe.reference_date = "2016-01-10" pe.party_type = "Supplier" diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json index b4d63f87e6..10e147ed08 100644 --- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json +++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json @@ -98,7 +98,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-07-11 03:28:03.420683", + "modified": "2018-02-21 03:28:03.420683", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Deduction", diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 4c7d8a7510..bfd6d54824 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -61,13 +61,11 @@ class TestPaymentRequest(unittest.TestCase): self.assertEqual(pr.currency, "USD") def test_payment_entry(self): - frappe.db.set_value("Company", "_Test Company", + frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC") - frappe.db.set_value("Company", "_Test Company", - "write_off_account", "_Test Write Off - _TC") - frappe.db.set_value("Company", "_Test Company", - "cost_center", "_Test Cost Center - _TC") - + frappe.db.set_value("Company", "_Test Company", "write_off_account", "_Test Write Off - _TC") + frappe.db.set_value("Company", "_Test Company", "cost_center", "_Test Cost Center - _TC") + so_inr = make_sales_order(currency="INR") pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com", mute_email=1, submit_doc=1, return_doc=1) @@ -82,15 +80,15 @@ class TestPaymentRequest(unittest.TestCase): pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com", mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1) - + pe = pr.set_as_paid() - + expected_gle = dict((d[0], d) for d in [ ["_Test Receivable USD - _TC", 0, 5000, si_usd.name], [pr.payment_account, 6290.0, 0, None], ["_Test Exchange Gain/Loss - _TC", 0, 1290, None] ]) - + gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s order by account asc""", pe.name, as_dict=1) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 6646506e74..d1f99ab3eb 100755 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -42,6 +42,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -70,6 +71,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -101,6 +103,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -131,6 +134,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -163,6 +167,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "300px" }, @@ -194,6 +199,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -225,6 +231,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -254,6 +261,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -284,6 +292,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -315,6 +324,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -345,6 +355,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -376,6 +387,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -404,6 +416,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -435,6 +448,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -465,6 +479,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -495,6 +510,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -523,6 +539,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -553,6 +570,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -583,6 +601,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -611,6 +630,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -641,6 +661,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -669,6 +690,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -701,6 +723,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -733,6 +756,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -761,6 +785,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -793,6 +818,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -825,6 +851,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -855,6 +882,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -884,6 +912,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -915,6 +944,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -946,6 +976,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -975,6 +1006,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1006,6 +1038,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1037,6 +1070,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1067,6 +1101,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1097,6 +1132,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1127,6 +1163,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1156,6 +1193,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1187,6 +1225,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1217,6 +1256,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1249,6 +1289,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1280,6 +1321,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1312,6 +1354,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1343,6 +1386,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1373,6 +1417,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1403,6 +1448,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1433,6 +1479,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1462,6 +1509,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1495,6 +1543,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "120px" }, @@ -1524,6 +1573,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1554,6 +1604,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1589,6 +1640,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "120px" }, @@ -1619,6 +1671,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1649,6 +1702,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1680,6 +1734,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1713,6 +1768,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1745,6 +1801,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1776,6 +1833,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -1809,6 +1867,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1840,6 +1899,39 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:parent.is_subcontracted == 'Yes'", + "fieldname": "include_exploded_items", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Include Exploded Items", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1868,6 +1960,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1898,6 +1991,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1930,6 +2024,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1961,6 +2056,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1993,6 +2089,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2022,6 +2119,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2053,6 +2151,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2083,6 +2182,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2113,6 +2213,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2143,6 +2244,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -2156,7 +2258,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-11-30 14:21:00.962126", + "modified": "2018-02-22 15:15:25.297672", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 93c22067d8..bb2d071dc2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -106,7 +106,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte on_submit: function(doc, dt, dn) { var me = this; - if (frappe.get_route()[0] != 'Sales Invoice') { + if (frappe.get_route()[0] != 'Form') { return } diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py index 028fa723dd..ddef65799b 100644 --- a/erpnext/accounts/doctype/tax_rule/tax_rule.py +++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py @@ -3,6 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals +from past.builtins import cmp import frappe from frappe import _ from frappe.model.document import Document diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 03a06cc5f2..0199e7d48a 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -65,6 +65,8 @@ def merge_similar_entries(gl_map): # filter zero debit and credit entries merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map) + merged_gl_map = list(merged_gl_map) + return merged_gl_map def check_if_in_list(gle, gl_map): diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index f3e52031ed..ffe860e8ec 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import re - +from past.builtins import cmp import functools import frappe diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 8f4cb53214..926331bbda 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -30,8 +30,7 @@ class PurchaseOrder(BuyingController): 'target_parent_field': 'per_ordered', 'target_ref_field': 'stock_qty', 'source_field': 'stock_qty', - 'percent_join_field': 'material_request', - 'overflow_type': 'order' + 'percent_join_field': 'material_request' }] def onload(self): @@ -242,12 +241,16 @@ class PurchaseOrder(BuyingController): pass def update_status_updater(self): - self.status_updater[0].update({ - "target_parent_dt": "Sales Order", - "target_dt": "Sales Order Item", + self.status_updater.append({ + 'source_dt': 'Purchase Order Item', + 'target_dt': 'Sales Order Item', 'target_field': 'ordered_qty', - "join_field": "sales_order_item", - "target_parent_field": '' + 'target_parent_dt': 'Sales Order', + 'target_parent_field': '', + 'join_field': 'sales_order_item', + 'source_dt': 'Purchase Order Item', + 'target_ref_field': 'stock_qty', + 'source_field': 'stock_qty' }) def update_delivered_qty_in_sales_order(self): diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 1a2842ac83..4927d06180 100755 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -43,6 +43,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -73,6 +74,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -104,6 +106,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -133,6 +136,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -164,6 +168,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -194,6 +199,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -224,6 +230,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -256,6 +263,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "300px" }, @@ -285,6 +293,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -315,6 +324,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -346,6 +356,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -375,6 +386,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -407,6 +419,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "60px" }, @@ -441,6 +454,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -471,6 +485,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -504,6 +519,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -538,6 +554,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -567,6 +584,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -597,6 +615,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -627,6 +646,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -655,6 +675,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -686,6 +707,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -716,6 +738,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -744,6 +767,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -776,6 +800,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -808,6 +833,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -836,6 +862,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -870,6 +897,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -903,6 +931,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -933,6 +962,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -962,6 +992,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -993,6 +1024,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1024,6 +1056,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1053,6 +1086,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1084,6 +1118,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1115,6 +1150,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1145,6 +1181,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1175,6 +1212,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1205,6 +1243,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1234,6 +1273,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1265,6 +1305,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1294,6 +1335,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1326,6 +1368,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1356,6 +1399,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1389,6 +1433,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "120px" }, @@ -1421,6 +1466,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1452,6 +1498,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1482,6 +1529,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1512,6 +1560,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1542,6 +1591,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1572,6 +1622,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1600,6 +1651,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1633,6 +1685,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1665,6 +1718,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1696,6 +1750,70 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "depends_on": "eval:parent.is_subcontracted == 'Yes'", + "fieldname": "include_exploded_items", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Include Exploded Items", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_56", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1728,6 +1846,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -1760,6 +1879,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1791,6 +1911,37 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_60", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1821,6 +1972,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1853,6 +2005,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1884,6 +2037,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -1897,7 +2051,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-12-14 09:36:40.837027", + "modified": "2018-02-22 15:43:18.746731", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py index 3c1884494a..b9161953ec 100644 --- a/erpnext/config/desktop.py +++ b/erpnext/config/desktop.py @@ -200,7 +200,7 @@ def get_data(): "color": "#fd784f", "icon": "octicon octicon-calendar", "label": _("Course Schedule"), - "link": "Calendar/Course Schedule", + "link": "List/Course Schedule/Calendar", "_doctype": "Course Schedule", "type": "list", "hidden": 1 diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py index 34a9b70c51..4e8bb483a2 100644 --- a/erpnext/config/healthcare.py +++ b/erpnext/config/healthcare.py @@ -86,6 +86,11 @@ def get_data(): "type": "doctype", "name": "Medical Code", "label": _("Medical Code"), + }, + { + "type": "doctype", + "name": "Patient Service Unit", + "label": _("Patient Service Unit") } ] }, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 1720b1a5e9..c6d911a26f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -11,6 +11,7 @@ from erpnext.utilities.transaction_base import TransactionBase from erpnext.controllers.sales_and_purchase_return import validate_return from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled from erpnext.exceptions import InvalidCurrency +from six import text_type force_item_fields = ("item_group", "brand", "stock_uom") @@ -666,6 +667,8 @@ class AccountsController(TransactionBase): self.remove(item) def set_payment_schedule(self): + if self.doctype == 'Sales Invoice' and self.is_pos: return + posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date") date = self.get("due_date") due_date = date or posting_date @@ -695,6 +698,8 @@ class AccountsController(TransactionBase): dates = [] li = [] + if self.doctype == 'Sales Invoice' and self.is_pos: return + for d in self.get("payment_schedule"): if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date): frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx)) @@ -708,6 +713,8 @@ class AccountsController(TransactionBase): .format(list=duplicates)) def validate_payment_schedule_amount(self): + if self.doctype == 'Sales Invoice' and self.is_pos: return + if self.get("payment_schedule"): total = 0 for d in self.get("payment_schedule"): @@ -926,7 +933,7 @@ def get_payment_terms(terms_template, posting_date=None, grand_total=None, bill_ @frappe.whitelist() def get_payment_term_details(term, posting_date=None, grand_total=None, bill_date=None): term_details = frappe._dict() - if isinstance(term, unicode): + if isinstance(term, text_type): term = frappe.get_doc("Payment Term", term) else: term_details.payment_term = term.payment_term diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index b141455b19..ff0f0c2b21 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -195,7 +195,11 @@ class BuyingController(StockController): self.set('supplied_items', []) def update_raw_materials_supplied(self, item, raw_material_table): - bom_items = self.get_items_from_bom(item.item_code, item.bom) + exploded_item = 1 + if hasattr(item, 'include_exploded_items'): + exploded_item = item.get('include_exploded_items') + + bom_items = get_items_from_bom(item.item_code, item.bom, exploded_item) raw_materials_cost = 0 items = list(set([d.item_code for d in bom_items])) item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse @@ -276,20 +280,6 @@ class BuyingController(StockController): if d not in delete_list: self.append(raw_material_table, d) - def get_items_from_bom(self, item_code, bom): - bom_items = frappe.db.sql("""select t2.item_code, - t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit, - t2.rate, t2.stock_uom, t2.name, t2.description, t2.source_warehouse - from `tabBOM` t1, `tabBOM Item` t2, tabItem t3 - where t2.parent = t1.name and t1.item = %s - and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s - and t2.item_code = t3.name and t3.is_stock_item = 1""", (item_code, bom), as_dict=1) - - if not bom_items: - msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1) - - return bom_items - @property def sub_contracted_items(self): if not hasattr(self, "_sub_contracted_items"): @@ -456,3 +446,21 @@ class BuyingController(StockController): else: frappe.throw(_("Please enter Reqd by Date")) +def get_items_from_bom(item_code, bom, exploded_item=1): + doctype = "BOM Item" if not exploded_item else "BOM Explosion Item" + + bom_items = frappe.db.sql("""select t2.item_code, t2.name, + t2.rate, t2.stock_uom, t2.source_warehouse, t2.description, + t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit + from + `tabBOM` t1, `tab{0}` t2, tabItem t3 + where + t2.parent = t1.name and t1.item = %s + and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s + and t2.item_code = t3.name and t3.is_stock_item = 1""".format(doctype), + (item_code, bom), as_dict=1) + + if not bom_items: + msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1) + + return bom_items \ No newline at end of file diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index d16f063c20..4b8bbee749 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -53,8 +53,9 @@ def validate_returned_items(doc): valid_items = frappe._dict() - select_fields = "item_code, qty, rate, parenttype" if doc.doctype=="Purchase Invoice" \ - else "item_code, qty, rate, serial_no, batch_no, parenttype" + select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor" + if doc.doctype != 'Purchase Invoice': + select_fields += ",serial_no, batch_no" if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']: select_fields += ",rejected_qty, received_qty" @@ -111,7 +112,7 @@ def validate_returned_items(doc): frappe.throw(_("Atleast one item should be entered with negative quantity in return document")) def validate_quantity(doc, args, ref, valid_items, already_returned_items): - fields = ['qty'] + fields = ['stock_qty'] if doc.doctype in ['Purchase Receipt', 'Purchase Invoice']: fields.extend(['received_qty', 'rejected_qty']) @@ -119,16 +120,19 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items): for column in fields: returned_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0 - reference_qty = ref.get(column) + reference_qty = (ref.get(column) if column == 'stock_qty' + else ref.get(column) * ref.get("conversion_factor", 1.0)) + max_returnable_qty = flt(reference_qty) - returned_qty label = column.replace('_', ' ').title() + if reference_qty: if flt(args.get(column)) > 0: frappe.throw(_("{0} must be negative in return document").format(label)) elif returned_qty >= reference_qty and args.get(column): frappe.throw(_("Item {0} has already been returned") .format(args.item_code), StockOverReturnError) - elif abs(args.get(column)) > max_returnable_qty: + elif (abs(args.get(column)) * args.get("conversion_factor", 1.0)) > max_returnable_qty: frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}") .format(args.idx, reference_qty, args.item_code), StockOverReturnError) @@ -138,6 +142,7 @@ def get_ref_item_dict(valid_items, ref_item_row): valid_items.setdefault(ref_item_row.item_code, frappe._dict({ "qty": 0, "rate": 0, + "stock_qty": 0, "rejected_qty": 0, "received_qty": 0, "serial_no": [], @@ -145,6 +150,7 @@ def get_ref_item_dict(valid_items, ref_item_row): })) item_dict = valid_items[ref_item_row.item_code] item_dict["qty"] += ref_item_row.qty + item_dict["stock_qty"] += ref_item_row.get('stock_qty', 0) if ref_item_row.get("rate", 0) > item_dict["rate"]: item_dict["rate"] = ref_item_row.get("rate", 0) @@ -161,9 +167,10 @@ def get_ref_item_dict(valid_items, ref_item_row): return valid_items def get_already_returned_items(doc): - column = 'child.item_code, sum(abs(child.qty)) as qty' + column = 'child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty' if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']: - column += ', sum(abs(child.rejected_qty)) as rejected_qty, sum(abs(child.received_qty)) as received_qty' + column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty, + sum(abs(child.received_qty) * child.conversion_factor) as received_qty""" data = frappe.db.sql(""" select {0} @@ -180,6 +187,7 @@ def get_already_returned_items(doc): for d in data: items.setdefault(d.item_code, frappe._dict({ "qty": d.get("qty"), + "stock_qty": d.get("stock_qty"), "received_qty": d.get("received_qty"), "rejected_qty": d.get("rejected_qty") })) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 2f54fc0175..b46c752aa6 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -250,7 +250,7 @@ class StatusUpdater(Document): if args['detail_id']: if not args.get("extra_cond"): args["extra_cond"] = "" - + frappe.db.sql("""update `tab%(target_dt)s` set %(target_field)s = ( (select ifnull(sum(%(source_field)s), 0) @@ -275,7 +275,7 @@ class StatusUpdater(Document): """Update percent field in parent transaction""" self._update_modified(args, update_modified) - + if args.get('target_parent_field'): frappe.db.sql("""update `tab%(target_parent_dt)s` set %(target_parent_field)s = round( diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 008ce6e1e3..047ef4a40e 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -157,7 +157,7 @@ class calculate_taxes_and_totals(object): return current_tax_fraction def _get_tax_rate(self, tax, item_tax_map): - if item_tax_map.has_key(tax.account_head): + if tax.account_head in item_tax_map: return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) else: return tax.rate diff --git a/erpnext/docs/assets/css/docs-erp.css b/erpnext/docs/assets/css/docs-erp.css new file mode 100644 index 0000000000..5ac7516084 --- /dev/null +++ b/erpnext/docs/assets/css/docs-erp.css @@ -0,0 +1,14 @@ +.embed-container { + position: relative; + padding-bottom: 56.25%; + height: 0; + overflow: hidden; + max-width: 100%; } + +.embed-container iframe, .embed-container object, .embed-container embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/erpnext/docs/assets/img/buying/supplier-new-address-contact.png b/erpnext/docs/assets/img/buying/supplier-new-address-contact.png new file mode 100644 index 0000000000..2a674e3095 Binary files /dev/null and b/erpnext/docs/assets/img/buying/supplier-new-address-contact.png differ diff --git a/erpnext/docs/assets/img/buying/supplier-payable-account.png b/erpnext/docs/assets/img/buying/supplier-payable-account.png new file mode 100644 index 0000000000..ce35ef816f Binary files /dev/null and b/erpnext/docs/assets/img/buying/supplier-payable-account.png differ diff --git a/erpnext/docs/assets/img/setup/users/add-user-details.png b/erpnext/docs/assets/img/setup/users/add-user-details.png new file mode 100644 index 0000000000..e9cbede137 Binary files /dev/null and b/erpnext/docs/assets/img/setup/users/add-user-details.png differ diff --git a/erpnext/docs/user/manual/en/CRM/customer.md b/erpnext/docs/user/manual/en/CRM/customer.md index 780881bd6f..5c158d1658 100644 --- a/erpnext/docs/user/manual/en/CRM/customer.md +++ b/erpnext/docs/user/manual/en/CRM/customer.md @@ -69,6 +69,16 @@ identify which Customers are profitable and which are not. Territories will help you set sales targets for the respective territories. You can also mention [Sales Person](/docs/user/manual/en/CRM/setup/sales-person.html) against a customer. + +
+ +
+ +
+
+ ### Sales Partner A Sales Partner is a third party distributor / dealer / commission agent / diff --git a/erpnext/docs/user/manual/en/CRM/index.md b/erpnext/docs/user/manual/en/CRM/index.md index 2cc7e98dc0..d5a79b3d2c 100644 --- a/erpnext/docs/user/manual/en/CRM/index.md +++ b/erpnext/docs/user/manual/en/CRM/index.md @@ -5,4 +5,12 @@ ERPNext helps you track business **Opportunities** from **Leads** and The CRM Module helps maintain Leads, Oppurtunities and Customers. +
+
+ +
+
+ + {index} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/CRM/newsletter.md b/erpnext/docs/user/manual/en/CRM/newsletter.md index e41ba1fc52..50453ffda8 100644 --- a/erpnext/docs/user/manual/en/CRM/newsletter.md +++ b/erpnext/docs/user/manual/en/CRM/newsletter.md @@ -18,4 +18,9 @@ send the email to all the intended receipients by clicking on the send button. Newsletter - Test +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/accounts/advance-payment-entry.md b/erpnext/docs/user/manual/en/accounts/advance-payment-entry.md index 4ae0a5ce09..6d822f1124 100644 --- a/erpnext/docs/user/manual/en/accounts/advance-payment-entry.md +++ b/erpnext/docs/user/manual/en/accounts/advance-payment-entry.md @@ -59,4 +59,10 @@ advance in the advances table. The accounting will be adjusted accordingly. Save and submit the Sales Invoice. + +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/accounts/budgeting.md b/erpnext/docs/user/manual/en/accounts/budgeting.md index 66021106ad..679c38e3e0 100644 --- a/erpnext/docs/user/manual/en/accounts/budgeting.md +++ b/erpnext/docs/user/manual/en/accounts/budgeting.md @@ -55,4 +55,9 @@ Accounts > Budget and Cost Center > Budget Variance Report Budget Variance Report +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/accounts/chart-of-accounts.md b/erpnext/docs/user/manual/en/accounts/chart-of-accounts.md index 79bc1c1ff2..5f9e611790 100644 --- a/erpnext/docs/user/manual/en/accounts/chart-of-accounts.md +++ b/erpnext/docs/user/manual/en/accounts/chart-of-accounts.md @@ -155,4 +155,14 @@ Typically, you might want to create Accounts for * Types of Sales (for example, Product Sales, Service Sales etc.) under Income. * Types of Assets (building, machinery, furniture etc.) under Fixed Assets. + +
+
+ +
+
+ + + {next} diff --git a/erpnext/docs/user/manual/en/accounts/index.md b/erpnext/docs/user/manual/en/accounts/index.md index d9e94c0d84..531775e0e7 100644 --- a/erpnext/docs/user/manual/en/accounts/index.md +++ b/erpnext/docs/user/manual/en/accounts/index.md @@ -10,6 +10,11 @@ In ERPNext, your accounting operations consists of 3 main transactions: * Purchase Invoice: Bills that your Suppliers give you for their products or services. * Journal Entries: For accounting entries, like payments, credit and other types. +
+ +
+ ### Topics {index} diff --git a/erpnext/docs/user/manual/en/accounts/opening-accounts.md b/erpnext/docs/user/manual/en/accounts/opening-accounts.md index 3261ccfd1c..99fb02e9fa 100644 --- a/erpnext/docs/user/manual/en/accounts/opening-accounts.md +++ b/erpnext/docs/user/manual/en/accounts/opening-accounts.md @@ -104,4 +104,12 @@ Here, select the company and type of invoice (sales or purchase) and add a line Opening Invoice Creation Tool + +
+
+ +
+
+ {next} diff --git a/erpnext/docs/user/manual/en/accounts/payment-terms.md b/erpnext/docs/user/manual/en/accounts/payment-terms.md index 4f28d0da17..ff1346698c 100644 --- a/erpnext/docs/user/manual/en/accounts/payment-terms.md +++ b/erpnext/docs/user/manual/en/accounts/payment-terms.md @@ -10,6 +10,10 @@ The documents you can attach Payment Terms to are: Note that the introduction of Payment Terms removes "Credit Days" and "Credit Days Based On" fields in Customer/Supplier master. Payment Term contains the same information and makes it more flexible to use. +
+ +
+ ## Payment Terms Navigate to the Payment Term list page and click "New". > Accounts > Payment Term > New Payment Term diff --git a/erpnext/docs/user/manual/en/accounts/tools/bank-reconciliation.md b/erpnext/docs/user/manual/en/accounts/tools/bank-reconciliation.md index 2f0ebaaa11..2d9acbbd7b 100644 --- a/erpnext/docs/user/manual/en/accounts/tools/bank-reconciliation.md +++ b/erpnext/docs/user/manual/en/accounts/tools/bank-reconciliation.md @@ -4,7 +4,7 @@ If you are receiving payments or making payments via cheques, the bank statements will not accurately match the dates of your entry, this is because the bank usually takes time to “clear” these payments. Also you may have mailed a cheque to your Supplier and it may be a few days before it is received and deposited by the Supplier. In ERPNext you can synchronise your bank statements and your Journal Entries using the “Bank Reconciliation” tool. -The Bank Reconciliation Report provide the difference between the bank balance shown in an organisation's bank statement, as provided by the ban against amount shown in the companies Chart of Accounts. +The Bank Reconciliation Report provide the difference between the bank balance shown in an organisation's bank statement, as provided by the bank against amount shown in the companies Chart of Accounts. ####Bank Reconciliation Statement diff --git a/erpnext/docs/user/manual/en/agriculture/index.md b/erpnext/docs/user/manual/en/agriculture/index.md index c49d1f20eb..932bb95948 100644 --- a/erpnext/docs/user/manual/en/agriculture/index.md +++ b/erpnext/docs/user/manual/en/agriculture/index.md @@ -17,6 +17,10 @@ The Agriculture module in ERPNext helps you keep track of your farming operation Task +
+ +
+ ### Topics {index} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/buying/index.md b/erpnext/docs/user/manual/en/buying/index.md index 55589ad43d..456dd6835c 100644 --- a/erpnext/docs/user/manual/en/buying/index.md +++ b/erpnext/docs/user/manual/en/buying/index.md @@ -10,6 +10,11 @@ profitability. ERPNext contains a set of transactions that will make your buying process as efficient and seamless as possible. +
+ +
+ ### Topics {index} diff --git a/erpnext/docs/user/manual/en/buying/request-for-quotation.md b/erpnext/docs/user/manual/en/buying/request-for-quotation.md index 2241ed60c6..660addc5c4 100644 --- a/erpnext/docs/user/manual/en/buying/request-for-quotation.md +++ b/erpnext/docs/user/manual/en/buying/request-for-quotation.md @@ -8,6 +8,11 @@ In ERPNext, You can create Request for Quotation directly by going to: After creation of Request for Quotation, there are two ways to generate Supplier Quotation from Request for Quotation. +
+ +
+ #### For User @@ -52,3 +57,5 @@ __Step 4:__ On submission, system will create Supplier Quotation (draft mode) ag If a supplier indicates that they will not provide a quotation for the item, this can be indicated in the RFQ document by checking the 'No Quote' box after the Request for Quotation has been submitted. ![Request For Quotation]({{docs_base_url}}/assets/img/buying/request-for-quotation.gif) + + diff --git a/erpnext/docs/user/manual/en/buying/supplier.md b/erpnext/docs/user/manual/en/buying/supplier.md index 7ff645f0c5..baf0fed525 100644 --- a/erpnext/docs/user/manual/en/buying/supplier.md +++ b/erpnext/docs/user/manual/en/buying/supplier.md @@ -8,6 +8,13 @@ You can create a new Supplier from: Supplier Master +
+
+ +
+
+ ### Contacts and Addresses Contacts and Addresses in ERPNext are stored separately so that you can create multiple Contacts and Addresses for a Suppliers. Once Supplier is saved, you will find option to create Contact and Address for that Supplier. diff --git a/erpnext/docs/user/manual/en/customize-erpnext/articles/making-custom-reports-in-erpnext.md b/erpnext/docs/user/manual/en/customize-erpnext/articles/making-custom-reports-in-erpnext.md index 5ef4248722..c35c86e70f 100644 --- a/erpnext/docs/user/manual/en/customize-erpnext/articles/making-custom-reports-in-erpnext.md +++ b/erpnext/docs/user/manual/en/customize-erpnext/articles/making-custom-reports-in-erpnext.md @@ -6,7 +6,10 @@ There are three kind of reports in ERPNext. Report Builder is an in-built report customization tool in ERPNext. This allows you to define specific fields of the form which shall be added in the report. Also you can set required filters, sorting and give preferred name to report. - +
+ +
### 2. Query Report diff --git a/erpnext/docs/user/manual/en/customize-erpnext/custom-field.md b/erpnext/docs/user/manual/en/customize-erpnext/custom-field.md index ec6c2229c8..dc4846b4fd 100644 --- a/erpnext/docs/user/manual/en/customize-erpnext/custom-field.md +++ b/erpnext/docs/user/manual/en/customize-erpnext/custom-field.md @@ -71,4 +71,9 @@ After inserting required details for the Custom Field, Update Customize Form. On Given a permission, user will be able to delete Custom Fields. Incase Custom Field is deleted by mistake, if you add another Custom Field with same name. Then you shall see new field auto-mapped with old-deleted Custom Field. +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/education/Assessment/assessment_criteria.md b/erpnext/docs/user/manual/en/education/Assessment/assessment_criteria.md index 8b35e8d378..c5740ca363 100644 --- a/erpnext/docs/user/manual/en/education/Assessment/assessment_criteria.md +++ b/erpnext/docs/user/manual/en/education/Assessment/assessment_criteria.md @@ -15,8 +15,6 @@ Assessment Criteria is be used when scheduling Assessment Plan for Student Group
-
diff --git a/erpnext/docs/user/manual/en/education/Assessment/assessment_group.md b/erpnext/docs/user/manual/en/education/Assessment/assessment_group.md index b1ad745ee9..b7599b84f3 100644 --- a/erpnext/docs/user/manual/en/education/Assessment/assessment_group.md +++ b/erpnext/docs/user/manual/en/education/Assessment/assessment_group.md @@ -15,8 +15,6 @@ On the same lines, you can also define multiple Assessment Group bases on assess
-
diff --git a/erpnext/docs/user/manual/en/education/Assessment/assessment_plan.md b/erpnext/docs/user/manual/en/education/Assessment/assessment_plan.md index 0bb7e6b36d..de13c80f7c 100644 --- a/erpnext/docs/user/manual/en/education/Assessment/assessment_plan.md +++ b/erpnext/docs/user/manual/en/education/Assessment/assessment_plan.md @@ -21,8 +21,6 @@ To schedule an assessment/examination for a Student Group, for specific Course,
-
diff --git a/erpnext/docs/user/manual/en/education/Assessment/assessment_result.md b/erpnext/docs/user/manual/en/education/Assessment/assessment_result.md index 4ca91a710b..a83a890463 100644 --- a/erpnext/docs/user/manual/en/education/Assessment/assessment_result.md +++ b/erpnext/docs/user/manual/en/education/Assessment/assessment_result.md @@ -8,8 +8,6 @@ Assessment Result is a log of marks/grades earned by the student for specific As
-
diff --git a/erpnext/docs/user/manual/en/education/Assessment/assessment_result_tool.md b/erpnext/docs/user/manual/en/education/Assessment/assessment_result_tool.md index f57fc182c1..39783258c9 100644 --- a/erpnext/docs/user/manual/en/education/Assessment/assessment_result_tool.md +++ b/erpnext/docs/user/manual/en/education/Assessment/assessment_result_tool.md @@ -11,8 +11,6 @@ As you go on entering marks for a Student, and switch to next student, in the ba
-
diff --git a/erpnext/docs/user/manual/en/education/Assessment/grading_scale.md b/erpnext/docs/user/manual/en/education/Assessment/grading_scale.md index 3d8f2e9ce3..790b04aa48 100644 --- a/erpnext/docs/user/manual/en/education/Assessment/grading_scale.md +++ b/erpnext/docs/user/manual/en/education/Assessment/grading_scale.md @@ -9,8 +9,6 @@ In the Grading Scale, you can define various grades and threshold for them. Base
-
diff --git a/erpnext/docs/user/manual/en/education/Attendance/student-attendance-tool.md b/erpnext/docs/user/manual/en/education/Attendance/student-attendance-tool.md index cc526b5f6c..4ad2ab8861 100644 --- a/erpnext/docs/user/manual/en/education/Attendance/student-attendance-tool.md +++ b/erpnext/docs/user/manual/en/education/Attendance/student-attendance-tool.md @@ -17,8 +17,6 @@ Student detials will be autofetched and you can mark the attendance of the given
-
diff --git a/erpnext/docs/user/manual/en/education/Attendance/student-attendance.md b/erpnext/docs/user/manual/en/education/Attendance/student-attendance.md index 0ebb65cd48..c9aba9fef1 100644 --- a/erpnext/docs/user/manual/en/education/Attendance/student-attendance.md +++ b/erpnext/docs/user/manual/en/education/Attendance/student-attendance.md @@ -17,8 +17,6 @@ Select the **Student, Course Schedule and Student Group** for which attendance i
-
diff --git a/erpnext/docs/user/manual/en/education/Attendance/student-leave-application.md b/erpnext/docs/user/manual/en/education/Attendance/student-leave-application.md index 406c20db3e..907dc8eaa6 100644 --- a/erpnext/docs/user/manual/en/education/Attendance/student-leave-application.md +++ b/erpnext/docs/user/manual/en/education/Attendance/student-leave-application.md @@ -15,8 +15,6 @@ Once a Leave Application is recorded for a student it will not be recorded in th
-
diff --git a/erpnext/docs/user/manual/en/education/admission/program-enrollment-tool.md b/erpnext/docs/user/manual/en/education/admission/program-enrollment-tool.md index fa2e09a7d8..e464d45b44 100644 --- a/erpnext/docs/user/manual/en/education/admission/program-enrollment-tool.md +++ b/erpnext/docs/user/manual/en/education/admission/program-enrollment-tool.md @@ -22,8 +22,6 @@ For promoting the students, the new academic year, academic term and program can
-
diff --git a/erpnext/docs/user/manual/en/education/admission/program-enrollment.md b/erpnext/docs/user/manual/en/education/admission/program-enrollment.md index 54bf98b521..8acff83e75 100644 --- a/erpnext/docs/user/manual/en/education/admission/program-enrollment.md +++ b/erpnext/docs/user/manual/en/education/admission/program-enrollment.md @@ -21,7 +21,6 @@ Student Category: For the Institutions having multiple Fees Structure, this fiel
-
diff --git a/erpnext/docs/user/manual/en/education/admission/student-applicant.md b/erpnext/docs/user/manual/en/education/admission/student-applicant.md index 2f725424e9..ec8184d644 100644 --- a/erpnext/docs/user/manual/en/education/admission/student-applicant.md +++ b/erpnext/docs/user/manual/en/education/admission/student-applicant.md @@ -32,7 +32,6 @@ the system shall create a student against that applicant and redirect you to the
-
diff --git a/erpnext/docs/user/manual/en/education/admission/student_admission.md b/erpnext/docs/user/manual/en/education/admission/student_admission.md index ff98a923e2..ede45a80f6 100644 --- a/erpnext/docs/user/manual/en/education/admission/student_admission.md +++ b/erpnext/docs/user/manual/en/education/admission/student_admission.md @@ -4,7 +4,7 @@ The admission process begins with filling the admission form. The Student Admiss To create a Student Admission record go to : -**education** >> **Admissions** >> **Student Admission** >> +> **education** > **Admissions** > **Student Admission** > Student Applicant diff --git a/erpnext/docs/user/manual/en/education/fees/fee-structure.md b/erpnext/docs/user/manual/en/education/fees/fee-structure.md index f502b93220..3218f17d34 100644 --- a/erpnext/docs/user/manual/en/education/fees/fee-structure.md +++ b/erpnext/docs/user/manual/en/education/fees/fee-structure.md @@ -24,8 +24,6 @@ If you are going to use this in the Fee Schedule, you must select the Accounts c
-
diff --git a/erpnext/docs/user/manual/en/education/schedule/course-schedule.md b/erpnext/docs/user/manual/en/education/schedule/course-schedule.md index eeccae456c..342e4c9c9b 100644 --- a/erpnext/docs/user/manual/en/education/schedule/course-schedule.md +++ b/erpnext/docs/user/manual/en/education/schedule/course-schedule.md @@ -9,8 +9,6 @@ You can see the overall course schedule in the Calendar view.
-
diff --git a/erpnext/docs/user/manual/en/education/schedule/scheduling-tool.md b/erpnext/docs/user/manual/en/education/schedule/scheduling-tool.md index 65dd95bd3a..6b306037fd 100644 --- a/erpnext/docs/user/manual/en/education/schedule/scheduling-tool.md +++ b/erpnext/docs/user/manual/en/education/schedule/scheduling-tool.md @@ -23,7 +23,6 @@ This tool can be used to create 'Course Schedules'. #### Video Tutorial on Scheduling Tool
-
diff --git a/erpnext/docs/user/manual/en/education/setup/course.md b/erpnext/docs/user/manual/en/education/setup/course.md index 609cac4c70..d55dbae3a3 100644 --- a/erpnext/docs/user/manual/en/education/setup/course.md +++ b/erpnext/docs/user/manual/en/education/setup/course.md @@ -14,9 +14,7 @@ The Course form is further linked to **Program, Student Group and Assessment Pla -
- +
diff --git a/erpnext/docs/user/manual/en/education/setup/instructor.md b/erpnext/docs/user/manual/en/education/setup/instructor.md index 9f4dbdca2c..32e5e215c7 100644 --- a/erpnext/docs/user/manual/en/education/setup/instructor.md +++ b/erpnext/docs/user/manual/en/education/setup/instructor.md @@ -20,8 +20,6 @@ Further, the log for the Instructor can be entered in the Instructor Log table w
-
diff --git a/erpnext/docs/user/manual/en/education/setup/program.md b/erpnext/docs/user/manual/en/education/setup/program.md index e9fb091164..5e476d2599 100644 --- a/erpnext/docs/user/manual/en/education/setup/program.md +++ b/erpnext/docs/user/manual/en/education/setup/program.md @@ -19,9 +19,7 @@ The Program Doctype is further linked to the **Student applicant**, **Program en #### Video Tutorial on Program and Courses -
- +
diff --git a/erpnext/docs/user/manual/en/human-resources/employee-advance.md b/erpnext/docs/user/manual/en/human-resources/employee-advance.md index 49f22e8c6e..46620f2e8b 100644 --- a/erpnext/docs/user/manual/en/human-resources/employee-advance.md +++ b/erpnext/docs/user/manual/en/human-resources/employee-advance.md @@ -29,4 +29,8 @@ On submission of payment entry, the paid amount and status will be updated in Em ### Adjust advances on Expense Claim Later when employee claims the expense and advance record can be fetched in Expense Claim and linked to the claim record. -Employee Advance Payment via Payment Entry \ No newline at end of file +Employee Advance Payment via Payment Entry + +
+ +
\ No newline at end of file diff --git a/erpnext/docs/user/manual/en/human-resources/employee-loan-management.md b/erpnext/docs/user/manual/en/human-resources/employee-loan-management.md index b76a2260c7..e85b5d6fa4 100644 --- a/erpnext/docs/user/manual/en/human-resources/employee-loan-management.md +++ b/erpnext/docs/user/manual/en/human-resources/employee-loan-management.md @@ -52,5 +52,9 @@ To auto deduct the Loan repayment from Salary, check "Repay from Salary" in Empl Salary Slip +
+ +
- \ No newline at end of file +{nex} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/human-resources/employee.md b/erpnext/docs/user/manual/en/human-resources/employee.md index 32e332a804..092d11d672 100644 --- a/erpnext/docs/user/manual/en/human-resources/employee.md +++ b/erpnext/docs/user/manual/en/human-resources/employee.md @@ -8,4 +8,8 @@ To create new Employee go to: Employee +
+ +
+ {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/human-resources/expense-claim.md b/erpnext/docs/user/manual/en/human-resources/expense-claim.md index 655935b59a..9f53da1372 100644 --- a/erpnext/docs/user/manual/en/human-resources/expense-claim.md +++ b/erpnext/docs/user/manual/en/human-resources/expense-claim.md @@ -11,6 +11,12 @@ To make a new Expense Claim, go to: Set the Employee ID, date and the list of expenses that are to be claimed and “Submit” the record. +
+ +
+ + ### Set Account for Employee Set employee's expense account on the employee form, system books an expense amount of an employee under this account. Expense Claim diff --git a/erpnext/docs/user/manual/en/human-resources/leave-application.md b/erpnext/docs/user/manual/en/human-resources/leave-application.md index b9855ab1c5..c735a6bae6 100644 --- a/erpnext/docs/user/manual/en/human-resources/leave-application.md +++ b/erpnext/docs/user/manual/en/human-resources/leave-application.md @@ -23,4 +23,10 @@ settings. See the earlier discussion on [Setting Up Permissions](/docs/user/manu for more info. You assign Leaves aginast an Employee check [Leave Allocation](/docs/user/manual/en/human-resources/leave.html) + +
+ +
+ {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/human-resources/salary-and-payroll.md b/erpnext/docs/user/manual/en/human-resources/salary-and-payroll.md index eff40efbf2..7e7aab12f8 100644 --- a/erpnext/docs/user/manual/en/human-resources/salary-and-payroll.md +++ b/erpnext/docs/user/manual/en/human-resources/salary-and-payroll.md @@ -169,6 +169,9 @@ created. Process Payroll - +
+ +
{next} diff --git a/erpnext/docs/user/manual/en/introduction/index.md b/erpnext/docs/user/manual/en/introduction/index.md index fcaf43e82a..05f8956f82 100644 --- a/erpnext/docs/user/manual/en/introduction/index.md +++ b/erpnext/docs/user/manual/en/introduction/index.md @@ -41,6 +41,12 @@ Among other things, ERPNext will help you to: And a lot lot lot more. +
+
+ +
+
### Topics diff --git a/erpnext/docs/user/manual/en/manufacturing/bill-of-materials.md b/erpnext/docs/user/manual/en/manufacturing/bill-of-materials.md index 11a7cac7bf..0557d2cf75 100644 --- a/erpnext/docs/user/manual/en/manufacturing/bill-of-materials.md +++ b/erpnext/docs/user/manual/en/manufacturing/bill-of-materials.md @@ -5,6 +5,12 @@ The **BOM** is a list of all materials (either bought or made) and operations that go into a finished product or sub-Item. In ERPNext, the component could have its own BOM hence forming a tree of Items with multiple levels. +
+ +
+ + To make accurate Purchase Requests, you must always maintain correct BOMs. To make a new BOM: diff --git a/erpnext/docs/user/manual/en/manufacturing/operation.md b/erpnext/docs/user/manual/en/manufacturing/operation.md index 75066aa606..bc94579c6a 100644 --- a/erpnext/docs/user/manual/en/manufacturing/operation.md +++ b/erpnext/docs/user/manual/en/manufacturing/operation.md @@ -10,4 +10,9 @@ You can also create a Operation by: Operation +
+ +
+ {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/manufacturing/production-order.md b/erpnext/docs/user/manual/en/manufacturing/production-order.md index 4ceb4fc480..4e2c129389 100644 --- a/erpnext/docs/user/manual/en/manufacturing/production-order.md +++ b/erpnext/docs/user/manual/en/manufacturing/production-order.md @@ -15,6 +15,11 @@ by: Production Order +
+ +
+ ### Creating Production Orders * Select the Item to be produced. diff --git a/erpnext/docs/user/manual/en/manufacturing/tools/production-planning-tool.md b/erpnext/docs/user/manual/en/manufacturing/tools/production-planning-tool.md index 9f253d6384..0887a8f1c9 100644 --- a/erpnext/docs/user/manual/en/manufacturing/tools/production-planning-tool.md +++ b/erpnext/docs/user/manual/en/manufacturing/tools/production-planning-tool.md @@ -3,6 +3,11 @@ Production Planning Tool helps you plan production and purchase of Items for a period (usually a week or a month). +
+ +
+ This list of Items can be generated from the open Sales Orders or pending Material Requests that can be Manufactured in the system and will generate: diff --git a/erpnext/docs/user/manual/en/manufacturing/workstation.md b/erpnext/docs/user/manual/en/manufacturing/workstation.md index 80654f43e3..7388e16ebc 100644 --- a/erpnext/docs/user/manual/en/manufacturing/workstation.md +++ b/erpnext/docs/user/manual/en/manufacturing/workstation.md @@ -18,4 +18,10 @@ While scheduling Production Order, system will check for the availability of the > Note : You can enable overtime for your workstation in [Manufacturing Settings](/docs/user/manual/en/manufacturing/setup/manufacturing-settings.html) + +
+ +
+ {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/projects/project.md b/erpnext/docs/user/manual/en/projects/project.md index 25f28d5878..82f0e6dd6f 100644 --- a/erpnext/docs/user/manual/en/projects/project.md +++ b/erpnext/docs/user/manual/en/projects/project.md @@ -4,6 +4,11 @@ Project management in ERPNext is Task driven. You can create Project and assign Project +
+ +
+ You can also track % Completion of a Project using different methods. 1. Task Completion diff --git a/erpnext/docs/user/manual/en/projects/tasks.md b/erpnext/docs/user/manual/en/projects/tasks.md index cae54f4194..0e529aaa4a 100644 --- a/erpnext/docs/user/manual/en/projects/tasks.md +++ b/erpnext/docs/user/manual/en/projects/tasks.md @@ -58,4 +58,8 @@ The system shall update the total amount from expense claims in the costing sect Task - Total Expense Claim +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md b/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md index 1524677d48..68b2a2f523 100644 --- a/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md +++ b/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md @@ -4,6 +4,11 @@ Timesheets can be tracked against Project and Tasks so that you can get reports Sales Invoice +
+ +
+ + ####Billable Timesheet To bill Customer based on Timesheet, check "Is Billable" in the Timesheet created against Project and Task. To learn more about billing Customer from Timesheet, click [here](/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.html). diff --git a/erpnext/docs/user/manual/en/selling/articles/drop-shipping.md b/erpnext/docs/user/manual/en/selling/articles/drop-shipping.md index 2d12ff4a21..d645ab1040 100644 --- a/erpnext/docs/user/manual/en/selling/articles/drop-shipping.md +++ b/erpnext/docs/user/manual/en/selling/articles/drop-shipping.md @@ -43,4 +43,9 @@ You can notify, Suppliers by sending a email after submitting Purchase Order by ###Video Help on Drop Ship - \ No newline at end of file +
+ +
+ +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/selling/index.md b/erpnext/docs/user/manual/en/selling/index.md index 36a99d2173..3fa3dcc0c1 100644 --- a/erpnext/docs/user/manual/en/selling/index.md +++ b/erpnext/docs/user/manual/en/selling/index.md @@ -9,3 +9,9 @@ organized and searchable manner. ### Topics {index} + + +
+ +
diff --git a/erpnext/docs/user/manual/en/selling/setup/item-price.md b/erpnext/docs/user/manual/en/selling/setup/item-price.md index adeeee453f..a8215a389b 100644 --- a/erpnext/docs/user/manual/en/selling/setup/item-price.md +++ b/erpnext/docs/user/manual/en/selling/setup/item-price.md @@ -4,11 +4,11 @@ Item Price is the record in which you can log sellig and buying rate of an item. There are two ways to reach to new Item Price form. -> Selling/Buying/Stock >> Setup >> Item Price >> New Item Price +> Selling/Buying/Stock > Setup > Item Price > New Item Price Or -> Item >> Add/Edit Prices >> Click on "+" >> New Item Price +> Item > Add/Edit Prices > Click on "+" >> New Item Price Following are the steps to create new Item Price. @@ -16,7 +16,7 @@ Step 1: Select Price List You can create multiple Price List in ERPNext to track Selling and Buying Price List of an item separtely. Also if item's selling prices id changing based on territory, or due to other criteria, you can create multiple selling Price List for it. -+Item Price list +Item Price list On selection of Price List, its currency and for selling or buying property will be fetched as well. @@ -26,20 +26,28 @@ Step 2: Select Item Select item for which Item Price record is to be created. On selection of Item Code, Item Name and Description will be fetched as well. -+Item Price list +Item Price list Step 3: Enter Rate Enter selling/buying rate of an item in Price List currency. -+Item Price list +Item Price list Step 4: Save Item Price To check all Item Price together, go to: -Stock >> Main Report >> Itemwise Price List Rate +> Stock > Main Report > Itemwise Price List Rate You will find option to create new Item Price record (+) in this report as well. +div> + +
+ +
+
+ {next} diff --git a/erpnext/docs/user/manual/en/setting-up/email/email-account.md b/erpnext/docs/user/manual/en/setting-up/email/email-account.md index d5aca1a006..62b8e87e72 100644 --- a/erpnext/docs/user/manual/en/setting-up/email/email-account.md +++ b/erpnext/docs/user/manual/en/setting-up/email/email-account.md @@ -42,4 +42,9 @@ If you would like ERPNext to notify you if an email is unreplied for a certain a Incoming EMail + +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/setting-up/print/letter-head.md b/erpnext/docs/user/manual/en/setting-up/print/letter-head.md index 72b79e1ad9..4a92965ac3 100644 --- a/erpnext/docs/user/manual/en/setting-up/print/letter-head.md +++ b/erpnext/docs/user/manual/en/setting-up/print/letter-head.md @@ -4,6 +4,11 @@ Each company has default Letter Head for their company. This Letter Head values In the Letter Head master, you can track Header and Footer details of the company. These details will appear in the Print Format of the transactions like Sales Order, Sales Invoice, Salary Slip, Purchase Order etc. +
+ +
+ ####Step 1: Go to Setup `Explore > Setup > Printing > Letter Head > New Letter Head` diff --git a/erpnext/docs/user/manual/en/setting-up/print/print-format-builder.md b/erpnext/docs/user/manual/en/setting-up/print/print-format-builder.md index f9799279af..039b4706d2 100644 --- a/erpnext/docs/user/manual/en/setting-up/print/print-format-builder.md +++ b/erpnext/docs/user/manual/en/setting-up/print/print-format-builder.md @@ -8,6 +8,11 @@ You can create a new Print Format either by going to: or Open the document for which you want to make a print format. Click the Printer icon, or go to Menu > Print and click on the **Edit** button. Note: You must have System Manager permission to do this. +
+ +
+ ### Step 1: Make a new Format Send Email diff --git a/erpnext/docs/user/manual/en/setting-up/print/print-headings.md b/erpnext/docs/user/manual/en/setting-up/print/print-headings.md index 7cf9b54c14..39b2657c1e 100644 --- a/erpnext/docs/user/manual/en/setting-up/print/print-headings.md +++ b/erpnext/docs/user/manual/en/setting-up/print/print-headings.md @@ -16,4 +16,10 @@ Example of a change in print heading is shown below: Print Heading + +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/setting-up/setting-up-taxes.md b/erpnext/docs/user/manual/en/setting-up/setting-up-taxes.md index 5a8b2d272e..2886c0573a 100644 --- a/erpnext/docs/user/manual/en/setting-up/setting-up-taxes.md +++ b/erpnext/docs/user/manual/en/setting-up/setting-up-taxes.md @@ -97,5 +97,13 @@ Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both. + +
+
+ +
+
+ {next} diff --git a/erpnext/docs/user/manual/en/setting-up/settings/naming-series.md b/erpnext/docs/user/manual/en/setting-up/settings/naming-series.md index 086a272821..652a6874c3 100644 --- a/erpnext/docs/user/manual/en/setting-up/settings/naming-series.md +++ b/erpnext/docs/user/manual/en/setting-up/settings/naming-series.md @@ -44,9 +44,8 @@ See how to set the naming series Naming Series -{next} - ### 4. Custom Field in Naming Series + Some companies prefers to make use of "short-codes" for suppliers, i.e. WN for company "Web Notes" that later can be used in naming series for quick identification. #### Example: @@ -55,3 +54,14 @@ See how to set the naming series Then under Naming Series, we should allow something like PO-.YY.MM.-.vendor_id.-.##### Resulting in "PO-1503-WN-00001" + + +
+
+ +
+
+ + +{next} diff --git a/erpnext/docs/user/manual/en/setting-up/users-and-permissions/adding-users.md b/erpnext/docs/user/manual/en/setting-up/users-and-permissions/adding-users.md index 6fb64df37e..65cf77c277 100644 --- a/erpnext/docs/user/manual/en/setting-up/users-and-permissions/adding-users.md +++ b/erpnext/docs/user/manual/en/setting-up/users-and-permissions/adding-users.md @@ -20,9 +20,12 @@ To add a new user, click on "New" Add user details such as First Name, Last Name, Email etc. +Add User Details + The user's Email will become the user id. Mobile No can also be used to log in if you check the Allow Login using Mobile No checkbox under the Security section in System Settings. While Mobile No will be unique, it will not be treated as a user id. Email Login + Mobile No Login After adding these details, save the user. diff --git a/erpnext/docs/user/manual/en/setting-up/users-and-permissions/index.md b/erpnext/docs/user/manual/en/setting-up/users-and-permissions/index.md index dcfd77649c..6c07761bbb 100644 --- a/erpnext/docs/user/manual/en/setting-up/users-and-permissions/index.md +++ b/erpnext/docs/user/manual/en/setting-up/users-and-permissions/index.md @@ -8,6 +8,13 @@ roles and permissions. The most important role is the "System Manager". Any user having this role can add other users and set roles to all users. +
+
+ +
+
+ ### Topics {index} diff --git a/erpnext/docs/user/manual/en/setting-up/workflows.md b/erpnext/docs/user/manual/en/setting-up/workflows.md index 06c47d7aa4..731fb3d48b 100644 --- a/erpnext/docs/user/manual/en/setting-up/workflows.md +++ b/erpnext/docs/user/manual/en/setting-up/workflows.md @@ -59,4 +59,9 @@ When the Leave Approver opens the Leave Application page, he can finally "Approv Workflow +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/stock/articles/index.txt b/erpnext/docs/user/manual/en/stock/articles/index.txt index acf1375214..4875de51e7 100644 --- a/erpnext/docs/user/manual/en/stock/articles/index.txt +++ b/erpnext/docs/user/manual/en/stock/articles/index.txt @@ -5,7 +5,7 @@ maintain-stock-field-frozen-in-item-master manage-rejected-finished-goods-items managing-batch-wise-inventory managing-fractions-in-uom -opening-stock-balance-entry-for-the-serialized-and-batch-item +opening-stock-balance-entry-for-serialized-and-batch-item repack-entry serial-no-naming stock-entry-purpose diff --git a/erpnext/docs/user/manual/en/stock/articles/managing-batch-wise-inventory.md b/erpnext/docs/user/manual/en/stock/articles/managing-batch-wise-inventory.md index 82408bd4e3..f66c8132aa 100644 --- a/erpnext/docs/user/manual/en/stock/articles/managing-batch-wise-inventory.md +++ b/erpnext/docs/user/manual/en/stock/articles/managing-batch-wise-inventory.md @@ -33,3 +33,10 @@ To check batch-wise stock balance report, go to: Stock > Standard Reports > Batch-wise Balance History Batchwise Stock Balance + +
+ +
+ +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/stock/articles/opening-stock-balance-entry-for-the-serialized-and-batch-item.md b/erpnext/docs/user/manual/en/stock/articles/opening-stock-balance-entry-for-serialized-and-batch-item.md similarity index 91% rename from erpnext/docs/user/manual/en/stock/articles/opening-stock-balance-entry-for-the-serialized-and-batch-item.md rename to erpnext/docs/user/manual/en/stock/articles/opening-stock-balance-entry-for-serialized-and-batch-item.md index 6ed111b99a..0661a2e12a 100644 --- a/erpnext/docs/user/manual/en/stock/articles/opening-stock-balance-entry-for-the-serialized-and-batch-item.md +++ b/erpnext/docs/user/manual/en/stock/articles/opening-stock-balance-entry-for-serialized-and-batch-item.md @@ -1,4 +1,4 @@ -#Opening Stock Balance Entry for the Serialized and Batch Item +#Opening Stock Balance Entry for Serialized and Batch Item Items for which Serial No. and Batch No. is maintained, opening stock balance entry for them is update via Stock Entry. [Click here to learn how serialized inventory is managed in ERPNext](/docs/user/manual/en/stock/serial-no.html). @@ -57,4 +57,11 @@ As per perpetual inventory valuation system, accounting entry is created for eve On submission of Stock Entry, stock ledger posting will be posted, and opening balance will be updated for the items on a given Posting Date. +
+
+ +
+
+ \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/stock/item/index.md b/erpnext/docs/user/manual/en/stock/item/index.md index 5b57c60ca1..b12a174a38 100644 --- a/erpnext/docs/user/manual/en/stock/item/index.md +++ b/erpnext/docs/user/manual/en/stock/item/index.md @@ -4,6 +4,13 @@ An Item is your companys' product or a service. The term Item is applicable to t ERPNext is optimised for itemised management of your sales and purchase. If you are in services, you can create an Item for each services that your offer. Completing the Item Master is very essential for successful implementation of ERPNext. +
+
+ +
+
+ ## Item Properties * **Item Name:** Item name is the actual name of your product or service. @@ -83,16 +90,10 @@ Inspection Criteria: If a Quality Inspection is prepared for this Item, then thi * **Default Income Account:** Income account selected here will be fetched automatically in sales invoice for this item. -Sales details - -Sales details - ***Cost Centre:** Cost center selected here will be fetched automatically in sales invoice for this item. ***Customer Codes:** Track Item Code assigned by the Customers for this Item. This will help you in searching item while creating Sales Order based on the Item Code in the Customer's Purchase Order. -Sales details - ### Manufacturing And Website Manufaturing details diff --git a/erpnext/docs/user/manual/en/stock/item/item-price.md b/erpnext/docs/user/manual/en/stock/item/item-price.md index 52850c2822..ce1a60ab4c 100644 --- a/erpnext/docs/user/manual/en/stock/item/item-price.md +++ b/erpnext/docs/user/manual/en/stock/item/item-price.md @@ -4,11 +4,11 @@ Item Price is the record in which you can log selling and buying rate of an item There are two ways to reach to new Item Price form. -> Selling/Buying/Stock >> Setup >> Item Price >> New Item Price +> Selling/Buying/Stock > Setup > Item Price > New Item Price Or -> Item >> Add/Edit Prices >> Click on "+" >> New Item Price +> Item > Add/Edit Prices > Click on "+" > New Item Price Following are the steps to create new Item Price. @@ -16,7 +16,7 @@ Step 1: Select Price List You can create multiple Price List in ERPNext to track Selling and Buying Price List of an item separtely. Also if item's selling prices id changing based on territory, or due to other criteria, you can create multiple selling Price List for it. -![Item Price list](/docs/assets/old_images/erpnext/item-price-list.png) +![Item Price list](/assets/img/stock/item-price-1.png) On selection of Price List, its currency and for selling or buying property will be fetched as well. @@ -26,20 +26,27 @@ Step 2: Select Item Select item for which Item Price record is to be created. On selection of Item Code, Item Name and Description will be fetched as well. -![Item Price Item](/docs/assets/old_images/erpnext/item-price-item.png) +![Item Price Item](/assets/img/stock/item-price-2.png) Step 3: Enter Rate Enter selling/buying rate of an item in Price List currency. -![Item Price Rate](/docs/assets/old_images/erpnext/item-price-rate.png) +![Item Price Rate](/assets/img/stock/item-price-3.png) Step 4: Save Item Price To check all Item Price together, go to: -Stock >> Main Report >> Itemwise Price List Rate +> Stock > Main Report > Itemwise Price List Rate You will find option to create new Item Price record (+) in this report as well. +
+
+ +
+
+ {next} diff --git a/erpnext/docs/user/manual/en/stock/item/item-variants.md b/erpnext/docs/user/manual/en/stock/item/item-variants.md index 08f88557d3..b5824279bd 100644 --- a/erpnext/docs/user/manual/en/stock/item/item-variants.md +++ b/erpnext/docs/user/manual/en/stock/item/item-variants.md @@ -56,3 +56,15 @@ To update the value in the variants items from the template item, select the res To set the fields Goto Stock > Item Variant Settings Item Variant Settings + +
+ +
+ +
+ +
+ +{next} diff --git a/erpnext/docs/user/manual/en/stock/item/purchase-details.md b/erpnext/docs/user/manual/en/stock/item/purchase-details.md index 134d551d46..cd695b8db6 100644 --- a/erpnext/docs/user/manual/en/stock/item/purchase-details.md +++ b/erpnext/docs/user/manual/en/stock/item/purchase-details.md @@ -4,4 +4,6 @@ To track a warranty period, it is necessary that the Item is a serialized Item. A warranty means a guarantee or a promise which provides assurance by one party to the other party which allows for a legal remedy if that promise is not true or followed. A warranty period is a time period in which a purchased product may be returned or exchanged. -Item Warranty \ No newline at end of file +Item Warranty + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/stock/item/reorder.md b/erpnext/docs/user/manual/en/stock/item/reorder.md index 548a8a9ad0..cd914125b4 100644 --- a/erpnext/docs/user/manual/en/stock/item/reorder.md +++ b/erpnext/docs/user/manual/en/stock/item/reorder.md @@ -10,4 +10,6 @@ For example, you can set your reorder level of Motherboard at 10. When there are For example, If reorder level is 100 items, your reorder quantity may not necessarily be 100 items. The Reorder quantity can be greater than or equal to reorder level. It may depend upon lead time, discount, transportation and average daily consumption. -Item Reorder \ No newline at end of file +Item Reorder + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/stock/material-request.md b/erpnext/docs/user/manual/en/stock/material-request.md index 96b9cea7e4..1c2eb9dcb6 100644 --- a/erpnext/docs/user/manual/en/stock/material-request.md +++ b/erpnext/docs/user/manual/en/stock/material-request.md @@ -32,4 +32,10 @@ The User can also raise a [Request For Quotation](/docs/user/manual/en/buying/re > Info: Material Request is not mandatory. It is ideal if you have centralized buying so that you can collect this information from various departments. + +
+ +
+ {next} diff --git a/erpnext/docs/user/manual/en/stock/opening-stock.md b/erpnext/docs/user/manual/en/stock/opening-stock.md index ea77999fe0..ff5384c766 100644 --- a/erpnext/docs/user/manual/en/stock/opening-stock.md +++ b/erpnext/docs/user/manual/en/stock/opening-stock.md @@ -12,4 +12,12 @@ If you are not making opening Stock Entry, you can select "Stock Adjustment" acc To understand Opening Stock for serialized Items visit [Stock Reconciliation](/docs/user/manual/en/setting-up/stock-reconciliation-for-non-serialized-item.html) + +
+
+ +
+
+ {next} diff --git a/erpnext/docs/user/manual/en/stock/serial-no.md b/erpnext/docs/user/manual/en/stock/serial-no.md index 015812497c..89bf734962 100644 --- a/erpnext/docs/user/manual/en/stock/serial-no.md +++ b/erpnext/docs/user/manual/en/stock/serial-no.md @@ -15,6 +15,11 @@ If your Item is _serialized_ you will have to enter the Serial Nos in the related column with each Serial No in a new line. You can maintain single units of serialized items using Serial Number. +
+ +
+ ### Serial Nos and Inventory Inventory of an Item can only be affected if the Serial No is transacted via a diff --git a/erpnext/docs/user/manual/en/stock/stock-entry.md b/erpnext/docs/user/manual/en/stock/stock-entry.md index e931b88a98..f04c6c3725 100644 --- a/erpnext/docs/user/manual/en/stock/stock-entry.md +++ b/erpnext/docs/user/manual/en/stock/stock-entry.md @@ -23,6 +23,11 @@ In the Stock Entry you have to update the Items table with all your transactions. For each row, you must enter a “Source Warehouse” or a “Target Warehouse” or both (if you are recording a movement). +
+ +
+ **Additional Costs:** If the stock entry is an incoming entry i.e any item is receiving at a target warehouse, you can add related additional costs (like Shipping Charges, Customs Duty, Operating Costs etc) associated with the process. The additional costs will be considered to calculate valuation rate of the items. diff --git a/erpnext/docs/user/manual/en/stock/tools/quality-inspection.md b/erpnext/docs/user/manual/en/stock/tools/quality-inspection.md index 3d0331a589..6ba39a96d7 100644 --- a/erpnext/docs/user/manual/en/stock/tools/quality-inspection.md +++ b/erpnext/docs/user/manual/en/stock/tools/quality-inspection.md @@ -7,4 +7,10 @@ Inspection. To enable ERPNext to perform this function, go to : Quality Inspection + +
+ +
+ {next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/website/add-products-to-website.md b/erpnext/docs/user/manual/en/website/add-products-to-website.md index bb5106f525..5d02083546 100644 --- a/erpnext/docs/user/manual/en/website/add-products-to-website.md +++ b/erpnext/docs/user/manual/en/website/add-products-to-website.md @@ -5,6 +5,11 @@ ERPNext will populate your website with products out of your Item Master. The html code will be generated automatically. +
+ +
+ #### Step 1: Edit Item For this example we will add a rocking chair to our catalog to sell on our website. diff --git a/erpnext/docs/user/manual/en/website/index.md b/erpnext/docs/user/manual/en/website/index.md index 2ac49558a8..817285f812 100644 --- a/erpnext/docs/user/manual/en/website/index.md +++ b/erpnext/docs/user/manual/en/website/index.md @@ -27,6 +27,10 @@ HTML / CSS or hire the services of a professional. The good part is that once this is setup, you can add and edit content, blogs and products directly from your ERP. +
+ +
+ ### Topics {index} diff --git a/erpnext/docs/user/manual/en/website/shopping-cart.md b/erpnext/docs/user/manual/en/website/shopping-cart.md index 26284f5237..09129bd9c3 100644 --- a/erpnext/docs/user/manual/en/website/shopping-cart.md +++ b/erpnext/docs/user/manual/en/website/shopping-cart.md @@ -8,6 +8,11 @@ the items you intend to buy. This software displays the price of the product . It also displays shipping and handling charges, along with taxes, if applicable. +
+ +
+ ### Shopping Cart Setup To set up a shopping cart, go to the website module. @@ -53,4 +58,10 @@ on "Place Order" to place the order. Website Specifications + +
+ +
+ {next} diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.js b/erpnext/education/doctype/fee_schedule/fee_schedule.js index a36b9cbe29..c4fff77d89 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.js +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.js @@ -38,9 +38,6 @@ frappe.ui.form.on('Fee Schedule', { if (data.reload && data.reload === 1) { frm.reload_doc(); } - if (data.progress && data.progress === "0") { - frappe.msgprint(__("Fee records will be created in the background. In case of any error the error message will be updated in the Schedule.")); - } if (data.progress) { let progress_bar = $(cur_frm.dashboard.progress_area).find(".progress-bar"); if (progress_bar) { @@ -74,6 +71,15 @@ frappe.ui.form.on('Fee Schedule', { }); }, "fa fa-play", "btn-success"); } + if (frm.doc.fee_creation_status == "Successful") { + frm.add_custom_button(__("View Fees Records"), function() { + frappe.route_options = { + fee_schedule: frm.doc.name + }; + frappe.set_route("List", "Fees"); + }); + } + }, fee_structure: function(frm) { diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.json b/erpnext/education/doctype/fee_schedule/fee_schedule.json index a77cc595ff..5aae690668 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.json +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.json @@ -4,7 +4,7 @@ "allow_import": 1, "allow_rename": 0, "autoname": "naming_series:", - "beta": 1, + "beta": 0, "creation": "2017-07-18 15:21:21.527136", "custom": 0, "docstatus": 0, @@ -1029,7 +1029,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-12-04 13:08:27.727709", + "modified": "2018-02-26 13:59:36.560780", "modified_by": "Administrator", "module": "Education", "name": "Fee Schedule", diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.py b/erpnext/education/doctype/fee_schedule/fee_schedule.py index 59acf462d9..b6df8c58d8 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.py +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.py @@ -56,8 +56,15 @@ class FeeSchedule(Document): self.db_set("fee_creation_status", "In Process") frappe.publish_realtime("fee_schedule_progress", {"progress": "0", "reload": 1}, user=frappe.session.user) - enqueue(generate_fee, queue='default', timeout=6000, event='generate_fee', - fee_schedule=self.name) + + total_records = sum([int(d.total_students) for d in self.student_groups]) + if total_records > 10: + frappe.msgprint(_('''Fee records will be created in the background. + In case of any error the error message will be updated in the Schedule.''')) + enqueue(generate_fee, queue='default', timeout=6000, event='generate_fee', + fee_schedule=self.name) + else: + generate_fee(self.name) def generate_fee(fee_schedule): doc = frappe.get_doc("Fee Schedule", fee_schedule) @@ -69,10 +76,7 @@ def generate_fee(fee_schedule): frappe.throw(_("Please setup Students under Student Groups")) for d in doc.student_groups: - students = frappe.db.sql(""" select sg.program, sg.batch, sgs.student, sgs.student_name - from `tabStudent Group` sg, `tabStudent Group Student` sgs - where sg.name=%s and sg.name=sgs.parent and sgs.active=1""", d.student_group, as_dict=1) - + students = get_students(d.student_group, doc.academic_year, doc.academic_term, doc.student_category) for student in students: try: fees_doc = get_mapped_doc("Fee Schedule", fee_schedule, { @@ -86,7 +90,7 @@ def generate_fee(fee_schedule): fees_doc.student = student.student fees_doc.student_name = student.student_name fees_doc.program = student.program - fees_doc.student_batch = student.batch + fees_doc.student_batch = student.student_batch_name fees_doc.send_payment_request = doc.send_email fees_doc.save() fees_doc.submit() @@ -110,6 +114,30 @@ def generate_fee(fee_schedule): {"progress": "100", "reload": 1}, user=frappe.session.user) +def get_students(student_group, academic_year, academic_term=None, student_category=None): + conditions = "" + if student_category: + conditions = " and pe.student_category='{}'".format(frappe.db.escape(student_category)) + if academic_term: + conditions = " and pe.academic_term='{}'".format(frappe.db.escape(academic_term)) + + students = frappe.db.sql(""" + select pe.student, pe.student_name, pe.program, pe.student_batch_name + from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe + where + pe.student = sgs.student and pe.academic_year = %s + and sgs.parent = %s and sgs.active = 1 + {conditions} + """.format(conditions=conditions), (academic_year, student_group), as_dict=1) + return students + + +@frappe.whitelist() +def get_total_students(student_group, academic_year, academic_term=None, student_category=None): + total_students = get_students(student_group, academic_year, academic_term, student_category) + return len(total_students) + + @frappe.whitelist() def get_fee_structure(source_name,target_doc=None): fee_request = get_mapped_doc("Fee Structure", source_name, @@ -117,23 +145,3 @@ def get_fee_structure(source_name,target_doc=None): "doctype": "Fee Schedule" }}, ignore_permissions=True) return fee_request - -@frappe.whitelist() -def get_total_students(student_group, academic_year, academic_term=None, student_category=None): - conditions = "" - if student_category: - conditions = " and pe.student_category='{}'".format(frappe.db.escape(student_category)) - if academic_term: - conditions = " and pe.academic_term='{}'".format(frappe.db.escape(academic_term)) - - - return frappe.db.sql(""" - select count(pe.name) - from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe - where - pe.student = sgs.student - and pe.academic_year = %s - and sgs.parent = %s - and sgs.active = 1 - {conditions} - """.format(conditions=conditions), (academic_year, student_group))[0][0] diff --git a/erpnext/education/doctype/guardian/guardian.json b/erpnext/education/doctype/guardian/guardian.json index d8722e2c90..191e92a0f9 100644 --- a/erpnext/education/doctype/guardian/guardian.json +++ b/erpnext/education/doctype/guardian/guardian.json @@ -2,7 +2,7 @@ "allow_copy": 0, "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, + "allow_rename": 1, "autoname": "GARD.####", "beta": 0, "creation": "2016-07-21 15:32:51.163292", @@ -507,7 +507,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-12-06 18:17:38.090252", + "modified": "2018-03-07 12:51:06.941609", "modified_by": "Administrator", "module": "Education", "name": "Guardian", diff --git a/erpnext/healthcare/doctype/consultation/consultation.json b/erpnext/healthcare/doctype/consultation/consultation.json index 184f484e24..c1bd37a3d2 100644 --- a/erpnext/healthcare/doctype/consultation/consultation.json +++ b/erpnext/healthcare/doctype/consultation/consultation.json @@ -767,7 +767,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Drug Prescription", + "label": "Medication", "length": 0, "no_copy": 0, "permlevel": 0, @@ -797,7 +797,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Drug Prescription", + "label": "Medication", "length": 0, "no_copy": 0, "options": "Drug Prescription", @@ -945,7 +945,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-12-28 11:25:35.256848", + "modified": "2018-02-19 11:35:13.826577", "modified_by": "Administrator", "module": "Healthcare", "name": "Consultation", diff --git a/erpnext/healthcare/doctype/consultation/consultation.py b/erpnext/healthcare/doctype/consultation/consultation.py index 7d41afe65e..451c4914b0 100755 --- a/erpnext/healthcare/doctype/consultation/consultation.py +++ b/erpnext/healthcare/doctype/consultation/consultation.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import frappe -from frappe import _ from frappe.model.document import Document from frappe.utils import getdate import json @@ -19,10 +18,6 @@ class Consultation(Document): def after_insert(self): insert_consultation_to_medical_record(self) - def on_submit(self): - if not self.diagnosis or not self.symptoms: - frappe.throw(_("Diagnosis and Complaints cannot be left blank")) - def on_cancel(self): if(self.appointment): frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open") diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py index 53f82e034c..7ad673d535 100644 --- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.healthcare.doctype.patient_appointment.patient_appointment import create_invoice +from erpnext.healthcare.doctype.patient_appointment.patient_appointment import invoice_appointment from frappe.utils.make_random import get_random from frappe.utils import nowdate, add_days # test_records = frappe.get_test_records('Fee Validity') @@ -14,6 +14,7 @@ class TestFeeValidity(unittest.TestCase): def test_fee_validity(self): patient = get_random("Patient") physician = get_random("Physician") + department = get_random("Medical Department") if not patient: patient = frappe.new_doc("Patient") @@ -22,33 +23,43 @@ class TestFeeValidity(unittest.TestCase): patient.save(ignore_permissions=True) patient = patient.name + if not department: + medical_department = frappe.new_doc("Medical Department") + medical_department.department = "Test Medical Department" + medical_department.save(ignore_permissions=True) + department = medical_department.name + if not physician: physician = frappe.new_doc("Physician") physician.first_name = "Amit Jain" + physician.department = department physician.save(ignore_permissions=True) physician = physician.name + + frappe.db.set_value("Healthcare Settings", None, "max_visit", 2) frappe.db.set_value("Healthcare Settings", None, "valid_days", 7) - appointment = create_appointment(patient, physician, nowdate()) + appointment = create_appointment(patient, physician, nowdate(), department) invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") self.assertEqual(invoice, None) - create_invoice(frappe.defaults.get_global_default("company"), physician, patient, appointment.name, appointment.appointment_date) - appointment = create_appointment(patient, physician, add_days(nowdate(), 4)) + invoice_appointment(appointment) + appointment = create_appointment(patient, physician, add_days(nowdate(), 4), department) invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") self.assertTrue(invoice) - appointment = create_appointment(patient, physician, add_days(nowdate(), 5)) + appointment = create_appointment(patient, physician, add_days(nowdate(), 5), department) invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") self.assertEqual(invoice, None) - appointment = create_appointment(patient, physician, add_days(nowdate(), 10)) + appointment = create_appointment(patient, physician, add_days(nowdate(), 10), department) invoice = frappe.db.get_value("Patient Appointment", appointment.name, "sales_invoice") self.assertEqual(invoice, None) -def create_appointment(patient, physician, appointment_date): +def create_appointment(patient, physician, appointment_date, department): appointment = frappe.new_doc("Patient Appointment") appointment.patient = patient appointment.physician = physician + appointment.department = department appointment.appointment_date = appointment_date appointment.save(ignore_permissions=True) return appointment diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 89121573f6..f34f7cfe1a 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -15,6 +15,20 @@ frappe.ui.form.on('Patient Appointment', { filters: {"disabled": 0} }; }); + frm.set_query("physician", function() { + return { + filters: { + 'department': frm.doc.department + } + }; + }); + frm.set_query("service_unit", function(){ + return { + filters: { + "is_group": false, + } + }; + }); if(frm.doc.patient){ frm.add_custom_button(__('Medical Record'), function() { frappe.route_options = {"patient": frm.doc.patient}; @@ -83,11 +97,10 @@ frappe.ui.form.on('Patient Appointment', { date: appointment_date }, callback: (r) => { - // console.log(r); var data = r.message; - if(data.available_slots.length > 0) { + if(data.slot_details.length > 0){ show_availability(data); - } else { + }else{ show_empty_state(); } } @@ -108,48 +121,71 @@ frappe.ui.form.on('Patient Appointment', { primary_action_label: __("Book"), primary_action: function() { // book slot - frm.set_value('appointment_time', selected_slot); - frm.set_value('duration', data.time_per_appointment); + var btn_selected = $wrapper.find('button.btn-selected-slot'); + frm.set_value('appointment_time', btn_selected.attr('data-name')); + frm.set_value('service_unit', btn_selected.attr('data-service-unit') || ''); + frm.set_value('duration', btn_selected.attr('data-duration')); d.hide(); frm.save(); + frm.enable_save(); } }); var $wrapper = d.fields_dict.available_slots.$wrapper; - var selected_slot = null; // disable dialog action initially d.get_primary_btn().attr('disabled', true); - // make buttons for each slot - var slot_html = data.available_slots.map(slot => { - return ``; - }).join(""); + var slot_details = data.slot_details; + var slot_html = ""; + $.each(slot_details, function(i, slot_detail){ + slot_html = slot_html + ``; + slot_html = slot_html + `
` + slot_detail['avail_slot'].map(slot => { + let disabled = ''; + let start_str = slot.from_time; + let start_time = moment(slot.from_time, 'HH:mm:ss'); + let to_time = moment(slot.to_time, 'HH:mm:ss'); + let interval = (to_time - start_time)/60000 | 0; + // iterate in all booked appointments, update the start time and duration + slot_detail['appointments'].forEach(function(booked) { + let booked_moment = moment(booked.appointment_time, 'HH:mm:ss'); + if(booked_moment.isSame(start_time) || booked_moment.isBetween(start_time, to_time)){ + if(booked.duration == 0){ + disabled = 'disabled="disabled"'; + return false; + } + start_time = booked_moment; + let end_time = booked_moment.add(booked.duration, 'minutes'); + if(end_time.isSameOrAfter(to_time)){ + disabled = 'disabled="disabled"'; + return false; + }else{ + start_str = end_time.format('HH:mm:ss'); + } + } + }); + return ``; + }).join(""); + slot_html = slot_html + `
`; + }); $wrapper .css('margin-bottom', 0) .addClass('text-center') .html(slot_html); - // disable buttons for which appointments are booked - data.appointments.map(slot => { - if(slot.status == "Scheduled" || slot.status == "Open" || slot.status == "Closed"){ - $wrapper - .find(`button[data-name="${slot.appointment_time}"]`) - .attr('disabled', true); - } - }); - // blue button when clicked $wrapper.on('click', 'button', function() { var $btn = $(this); $wrapper.find('button').removeClass('btn-primary'); + $wrapper.find('button').removeClass('btn-selected-slot'); $btn.addClass('btn-primary'); - selected_slot = $btn.attr('data-name'); - + $btn.addClass('btn-selected-slot'); // enable dialog action d.get_primary_btn().attr('disabled', null); }); @@ -209,12 +245,9 @@ var btn_update_status = function(frm, status){ }; var btn_invoice_consultation = function(frm){ - var doc = frm.doc; frappe.call({ - method: - "erpnext.healthcare.doctype.patient_appointment.patient_appointment.create_invoice", - args: {company: doc.company, physician:doc.physician, patient: doc.patient, - appointment_id: doc.name, appointment_date:doc.appointment_date }, + doc: frm.doc, + method:"create_invoice", callback: function(data){ if(!data.exc){ if(data.message){ diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 1dcdbb57f1..022a9d6d49 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -42,6 +42,39 @@ "reqd": 1, "search_index": 1, "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Medical Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 1, + "set_only_once": 1, + "translatable": 0, "unique": 0 }, { @@ -73,6 +106,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 1, + "translatable": 0, "unique": 0 }, { @@ -103,6 +137,71 @@ "reqd": 1, "search_index": 1, "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "appointment_time", + "fieldtype": "Time", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Time", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "description": "In Minutes", + "fieldname": "duration", + "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Duration", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -134,6 +233,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -151,8 +251,42 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, + "label": "", "length": 0, "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "service_unit", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Service Unit", + "length": 0, + "no_copy": 0, + "options": "Patient Service Unit", "permlevel": 0, "precision": "", "print_hide": 0, @@ -162,7 +296,8 @@ "report_hide": 0, "reqd": 0, "search_index": 0, - "set_only_once": 0, + "set_only_once": 1, + "translatable": 0, "unique": 0 }, { @@ -195,6 +330,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -226,6 +362,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -257,6 +394,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -271,7 +409,7 @@ "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, - "in_filter": 0, + "in_filter": 1, "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, @@ -289,6 +427,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -319,7 +458,8 @@ "report_hide": 0, "reqd": 0, "search_index": 0, - "set_only_once": 1, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -349,68 +489,8 @@ "report_hide": 0, "reqd": 0, "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "appointment_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, "set_only_once": 1, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "In Minutes", - "fieldname": "duration", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Duration", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -441,6 +521,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -470,37 +551,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 1, + "translatable": 0, "unique": 0 }, { @@ -532,6 +583,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -563,6 +615,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -593,6 +646,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -623,6 +677,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -654,6 +709,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 1, + "translatable": 0, "unique": 0 }, { @@ -685,6 +741,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -698,7 +755,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-12-28 11:26:20.262978", + "modified": "2018-02-26 12:44:33.756124", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", @@ -744,6 +801,26 @@ "share": 1, "submit": 0, "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Nursing User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 } ], "quick_entry": 0, diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 90d4d0ea20..926dc28373 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document import json -from frappe.utils import getdate, cint +from frappe.utils import getdate from frappe import _ import datetime from frappe.core.doctype.sms_settings.sms_settings import send_sms @@ -40,13 +40,8 @@ class PatientAppointment(Document): frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) confirm_sms(self) - def save(self, *args, **kwargs): - # duration is the only changeable field in the document - if not self.is_new(): - self.db_set('duration', cint(self.duration)) - else: - super(PatientAppointment, self).save(*args, **kwargs) - + def create_invoice(self): + return invoice_appointment(self) def appointment_cancel(appointment_id): appointment = frappe.get_doc("Patient Appointment", appointment_id) @@ -79,9 +74,9 @@ def get_availability_data(date, physician): weekday = date.strftime("%A") available_slots = [] - physician_schedule_name = None + slot_details = [] physician_schedule = None - time_per_appointment = None + employee = None physician_obj = frappe.get_doc("Physician", physician) @@ -112,43 +107,57 @@ def get_availability_data(date, physician): frappe.throw(_("Dr {0} on Leave on {1}").format(physician, date)) # get physicians schedule - physician_schedule_name = frappe.db.get_value("Physician", physician, "physician_schedule") - if physician_schedule_name: - physician_schedule = frappe.get_doc("Physician Schedule", physician_schedule_name) - time_per_appointment = frappe.db.get_value("Physician", physician, "time_per_appointment") + if physician_obj.physician_schedules: + for schedule in physician_obj.physician_schedules: + if schedule.schedule: + physician_schedule = frappe.get_doc("Physician Schedule", schedule.schedule) + else: + frappe.throw(_("Dr {0} does not have a Physician Schedule. Add it in Physician master".format(physician))) + + if physician_schedule: + available_slots = [] + for t in physician_schedule.time_slots: + if weekday == t.day: + available_slots.append(t) + + if available_slots: + appointments = [] + + if schedule.service_unit: + slot_name = schedule.schedule+" - "+schedule.service_unit + allow_overlap = frappe.get_value('Patient Service Unit', schedule.service_unit, 'overlap_appointments') + if allow_overlap: + # fetch all appointments to physician by service unit + appointments = frappe.get_all( + "Patient Appointment", + filters={"physician": physician, "service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]}, + fields=["name", "appointment_time", "duration", "status"]) + else: + # fetch all appointments to service unit + appointments = frappe.get_all( + "Patient Appointment", + filters={"service_unit": schedule.service_unit, "appointment_date": date, "status": ["not in",["Cancelled"]]}, + fields=["name", "appointment_time", "duration", "status"]) + else: + slot_name = schedule.schedule + # fetch all appointments to physician without service unit + appointments = frappe.get_all( + "Patient Appointment", + filters={"physician": physician, "service_unit": '', "appointment_date": date, "status": ["not in",["Cancelled"]]}, + fields=["name", "appointment_time", "duration", "status"]) + + slot_details.append({"slot_name":slot_name, "service_unit":schedule.service_unit, + "avail_slot":available_slots, 'appointments': appointments}) + else: frappe.throw(_("Dr {0} does not have a Physician Schedule. Add it in Physician master".format(physician))) - if physician_schedule: - for t in physician_schedule.time_slots: - if weekday == t.day: - available_slots.append(t) - - # `time_per_appointment` should never be None since validation in `Patient` is supposed to prevent - # that. However, it isn't impossible so we'll prepare for that. - if not time_per_appointment: - frappe.throw(_('"Time Per Appointment" hasn"t been set for Dr {0}. Add it in Physician master.').format(physician)) - - # if physician not available return - if not available_slots: + if not available_slots and not slot_details: # TODO: return available slots in nearby dates frappe.throw(_("Physician not available on {0}").format(weekday)) - # if physician on leave return - - # if holiday return - # if is_holiday(weekday): - - # get appointments on that day for physician - appointments = frappe.get_all( - "Patient Appointment", - filters={"physician": physician, "appointment_date": date}, - fields=["name", "appointment_time", "duration", "status"]) - return { - "available_slots": available_slots, - "appointments": appointments, - "time_per_appointment": time_per_appointment + "slot_details": slot_details } @@ -182,25 +191,25 @@ def confirm_sms(doc): @frappe.whitelist() -def create_invoice(company, physician, patient, appointment_id, appointment_date): - if not appointment_id: +def invoice_appointment(appointment_doc): + if not appointment_doc.name: return False sales_invoice = frappe.new_doc("Sales Invoice") - sales_invoice.customer = frappe.get_value("Patient", patient, "customer") - sales_invoice.appointment = appointment_id + sales_invoice.customer = frappe.get_value("Patient", appointment_doc.patient, "customer") + sales_invoice.appointment = appointment_doc.name sales_invoice.due_date = getdate() sales_invoice.is_pos = '0' - sales_invoice.debit_to = get_receivable_account(company) + sales_invoice.debit_to = get_receivable_account(appointment_doc.company) - fee_validity = get_fee_validity(physician, patient, appointment_date) - create_invoice_items(appointment_id, physician, company, sales_invoice) + fee_validity = get_fee_validity(appointment_doc.physician, appointment_doc.patient, appointment_doc.appointment_date) + create_invoice_items(appointment_doc.physician, appointment_doc.company, sales_invoice) sales_invoice.save(ignore_permissions=True) - frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (sales_invoice.name, appointment_id)) + frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (sales_invoice.name, appointment_doc.name)) frappe.db.set_value("Fee Validity", fee_validity.name, "ref_invoice", sales_invoice.name) consultation = frappe.db.exists({ "doctype": "Consultation", - "appointment": appointment_id}) + "appointment": appointment_doc.name}) if consultation: frappe.db.set_value("Consultation", consultation[0][0], "invoice", sales_invoice.name) return sales_invoice.name @@ -247,7 +256,7 @@ def create_fee_validity(physician, patient, date): return fee_validity -def create_invoice_items(appointment_id, physician, company, invoice): +def create_invoice_items(physician, company, invoice): item_line = invoice.append("items") item_line.item_name = "Consulting Charges" item_line.description = "Consulting Charges: " + physician @@ -326,4 +335,4 @@ def get_events(start, end, filters=None): {"start": start, "end": end}, as_dict=True, update={"allDay": 0}) for item in data: item.end = item.start + datetime.timedelta(minutes = item.duration) - return data + return data \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_service_unit/__init__.py b/erpnext/healthcare/doctype/patient_service_unit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.js b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.js new file mode 100644 index 0000000000..197b4e5dbc --- /dev/null +++ b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.js @@ -0,0 +1,15 @@ +// Copyright (c) 2017, earthians and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Patient Service Unit', { +}); + +// get query select patient service unit +cur_frm.fields_dict['parent_patient_service_unit'].get_query = function(doc) { + return{ + filters:[ + ['Patient Service Unit', 'is_group', '=', 1], + ['Patient Service Unit', 'name', '!=', doc.patient_service_unit_name] + ] + }; +}; diff --git a/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.json b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.json new file mode 100644 index 0000000000..0ca1a53e76 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.json @@ -0,0 +1,358 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 1, + "autoname": "field:patient_service_unit_name", + "beta": 1, + "creation": "2016-09-21 13:48:14.731437", + "custom": 0, + "description": "Patinet Service Unit", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "patient_service_unit_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Service Unit", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "parent_patient_service_unit", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Parent Service Unit", + "length": 0, + "no_copy": 0, + "options": "Patient Service Unit", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "is_group", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Is Group", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "overlap_appointments", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow Overlap", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 1, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 1, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "lft", + "fieldtype": "Int", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "lft", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 1, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rgt", + "fieldtype": "Int", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "rgt", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 1, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "old_parent", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Old Parent", + "length": 0, + "no_copy": 1, + "options": "Patient Service Unit", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 1, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-03-07 13:25:51.163029", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Patient Service Unit", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 1, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Nursing User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "user_permission_doctypes": "[\"Service Unit\"]", + "write": 0 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Medical Administrator", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Healthcare", + "search_fields": "patient_service_unit_name", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "patient_service_unit_name", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.py b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.py new file mode 100644 index 0000000000..6c177d82d2 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, earthians and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals + +from frappe.utils.nestedset import NestedSet + +class PatientServiceUnit(NestedSet): + nsm_parent_field = 'parent_patient_service_unit' + + def on_update(self): + super(PatientServiceUnit, self).on_update() + self.validate_one_root() diff --git a/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit_tree.js b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit_tree.js new file mode 100644 index 0000000000..0b03f2db8b --- /dev/null +++ b/erpnext/healthcare/doctype/patient_service_unit/patient_service_unit_tree.js @@ -0,0 +1,3 @@ +frappe.treeview_settings["Patient Service Unit"] = { + ignore_fields:["parent_patient_service_unit"] +}; diff --git a/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.js b/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.js new file mode 100644 index 0000000000..320388a1fd --- /dev/null +++ b/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Patient Service Unit", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Patient Service Unit + () => frappe.tests.make('Patient Service Unit', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.py b/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.py new file mode 100644 index 0000000000..ceb49fd4c6 --- /dev/null +++ b/erpnext/healthcare/doctype/patient_service_unit/test_patient_service_unit.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, earthians and Contributors +# See license.txt +from __future__ import unicode_literals +import unittest + +class TestPatientServiceUnit(unittest.TestCase): + pass diff --git a/erpnext/healthcare/doctype/physician/physician.js b/erpnext/healthcare/doctype/physician/physician.js index c224b5d36e..6ce01991f3 100755 --- a/erpnext/healthcare/doctype/physician/physician.js +++ b/erpnext/healthcare/doctype/physician/physician.js @@ -19,6 +19,13 @@ frappe.ui.form.on('Physician', { if(!frm.is_new()) { frappe.contacts.render_address_and_contact(frm); } + frm.set_query("service_unit", "physician_schedules", function(){ + return { + filters: { + "is_group": false, + } + }; + }); } }); diff --git a/erpnext/healthcare/doctype/physician/physician.json b/erpnext/healthcare/doctype/physician/physician.json index 1d9794e6de..e29561e91e 100644 --- a/erpnext/healthcare/doctype/physician/physician.json +++ b/erpnext/healthcare/doctype/physician/physician.json @@ -435,45 +435,14 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "physician_schedule", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Physician Schedule", - "length": 0, - "no_copy": 0, - "options": "Physician Schedule", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "column_break_17", - "fieldtype": "Column Break", + "fieldname": "physician_schedules", + "fieldtype": "Table", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -481,39 +450,10 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, + "label": "Physician Schedules", "length": 0, "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "In minutes", - "fieldname": "time_per_appointment", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time per Appointment", - "length": 0, - "no_copy": 0, + "options": "Physician Service Unit Schedule", "permlevel": 0, "precision": "", "print_hide": 0, @@ -811,7 +751,7 @@ "istable": 0, "max_attachments": 0, "modified": "2018-01-19 15:25:43.166877", - "modified_by": "jams@hcf.com", + "modified_by": "Administrator", "module": "Healthcare", "name": "Physician", "name_case": "", diff --git a/erpnext/healthcare/doctype/physician/physician.py b/erpnext/healthcare/doctype/physician/physician.py index 4d035d39f6..eb03083a49 100644 --- a/erpnext/healthcare/doctype/physician/physician.py +++ b/erpnext/healthcare/doctype/physician/physician.py @@ -20,7 +20,6 @@ class Physician(Document): [cstr(self.get(f)).strip() for f in ["first_name","middle_name","last_name"]])) def validate(self): - self.validate_schedule_and_time() validate_party_accounts(self) if self.user_id: @@ -37,15 +36,6 @@ class Physician(Document): frappe.permissions.remove_user_permission( "Physician", self.name, existing_user_id) - def validate_schedule_and_time(self): - if (self.physician_schedule or self.time_per_appointment) and \ - not (self.physician_schedule and self.time_per_appointment): - frappe.msgprint( - _('Both "Physician Schedule" and Time Per Appointment" must be set for Dr {0}').format( - self.first_name), - title='Error', raise_exception=1, indicator='red' - ) - def on_update(self): if self.user_id: frappe.permissions.add_user_permission("Physician", self.name, self.user_id) diff --git a/erpnext/healthcare/doctype/physician/test_physician.py b/erpnext/healthcare/doctype/physician/test_physician.py index 4bd497a4e9..2fbf5741cf 100644 --- a/erpnext/healthcare/doctype/physician/test_physician.py +++ b/erpnext/healthcare/doctype/physician/test_physician.py @@ -12,23 +12,6 @@ class TestPhysician(unittest.TestCase): def tearDown(self): frappe.delete_doc_if_exists('Physician', '_Testdoctor2', force=1) - def test_schedule_and_time(self): - physician = frappe.new_doc('Physician') - physician.first_name = '_Testdoctor2' - physician.physician_schedule = '_Testdoctor2 Schedule' - - self.assertRaises(frappe.ValidationError, physician.insert) - - physician.physician_schedule = '' - physician.time_per_appointment = 15 - - self.assertRaises(frappe.ValidationError, physician.insert) - - physician.physician_schedule = '_Testdoctor2 Schedule' - physician.time_per_appointment = 15 - - physician.insert() - def test_new_physician_without_schedule(self): physician = frappe.new_doc('Physician') physician.first_name = '_Testdoctor2' diff --git a/erpnext/healthcare/doctype/physician_service_unit_schedule/__init__.py b/erpnext/healthcare/doctype/physician_service_unit_schedule/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.json b/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.json new file mode 100644 index 0000000000..69fe7b3104 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.json @@ -0,0 +1,103 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2017-11-16 12:19:17.163786", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "schedule", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Schedule", + "length": 0, + "no_copy": 0, + "options": "Physician Schedule", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "service_unit", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Service Unit", + "length": 0, + "no_copy": 0, + "options": "Patient Service Unit", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2017-12-27 10:57:42.301295", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Physician Service Unit Schedule", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.py b/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.py new file mode 100644 index 0000000000..7aaec4de13 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_service_unit_schedule/physician_service_unit_schedule.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class PhysicianServiceUnitSchedule(Document): + pass diff --git a/erpnext/hr/doctype/employee/employee.js b/erpnext/hr/doctype/employee/employee.js index c24a6bc30b..5e1013d6a2 100755 --- a/erpnext/hr/doctype/employee/employee.js +++ b/erpnext/hr/doctype/employee/employee.js @@ -81,7 +81,7 @@ frappe.ui.form.on('Employee',{ } frappe.call({ method: "erpnext.hr.doctype.employee.employee.create_user", - args: { employee: cur_frm.doc.name }, + args: { employee: frm.doc.name, email: frm.doc.prefered_email }, callback: function(r) { frm.set_value("user_id", r.message) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 9541b775a1..25d3ec4bc9 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -263,7 +263,7 @@ def deactivate_sales_person(status = None, employee = None): frappe.db.set_value("Sales Person", sales_person, "enabled", 0) @frappe.whitelist() -def create_user(employee, user = None): +def create_user(employee, user = None, email=None): emp = frappe.get_doc("Employee", employee) employee_name = emp.employee_name.split(" ") @@ -277,6 +277,9 @@ def create_user(employee, user = None): first_name = employee_name[0] + if email: + emp.prefered_email = email + user = frappe.new_doc("User") user.update({ "name": emp.employee_name, diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index f6b5cc3fb2..31045b73a5 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -41,10 +41,12 @@ class ExpenseClaim(AccountsController): }[cstr(self.docstatus or 0)] paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount) - if self.total_sanctioned_amount > 0 and self.total_sanctioned_amount == paid_amount\ + + if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 + and flt(self.total_sanctioned_amount) == paid_amount)) \ and self.docstatus == 1: - self.status = "Paid" - elif self.total_sanctioned_amount > 0 and self.docstatus == 1: + self.status = "Paid" + elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1: self.status = "Unpaid" def set_payable_account(self): diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index 2435d06979..8f9c3d0f1d 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -3,7 +3,7 @@ "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, - "autoname": "LAP/.#####", + "autoname": "naming_series:", "beta": 0, "creation": "2013-02-20 11:18:11", "custom": 0, @@ -42,6 +42,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 1, + "translatable": 0, "unique": 0 }, { @@ -72,6 +73,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -101,6 +103,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -130,6 +133,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -159,6 +163,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -188,6 +193,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -217,6 +223,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -248,6 +255,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -278,6 +286,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -307,6 +316,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "50%" }, @@ -337,6 +347,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -366,6 +377,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -396,6 +408,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -425,6 +438,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -454,6 +468,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -484,6 +499,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -514,6 +530,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -542,6 +559,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -572,6 +590,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -602,6 +621,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -632,6 +652,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -646,7 +667,7 @@ "issingle": 0, "istable": 0, "max_attachments": 3, - "modified": "2018-02-12 13:10:05.766762", + "modified": "2018-03-04 18:24:04.863555", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 22fd0e5f98..eb739466d6 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -34,14 +34,13 @@ class LeaveApplication(Document): self.validate_salary_processed_days() self.validate_attendance() + if hasattr(self, "workflow_state") and self.workflow_state == "Rejected": + # notify leave applier about rejection + self.notify_employee() + def on_submit(self): - self.validate_back_dated_application() - def on_cancel(self): - # notify leave applier about cancellation - self.notify_employee("cancelled") - def validate_dates(self): if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)): frappe.throw(_("To date cannot be before from date")) @@ -217,7 +216,8 @@ class LeaveApplication(Document): # for post in messages "message": _get_message(url=True), "message_to": employee.user_id, - "subject": (_("Leave Application") + ": %s - %s") % (self.name) + "subject": (_("Leave Application") + ": %s - %s") % (self.name, + self.workflow_state if hasattr(self, "workflow_state") else "") }) def _get_message(url=False): diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/hr/doctype/payroll_entry/payroll_entry.py index a023cc3ea9..c6e531438f 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.py @@ -175,7 +175,7 @@ class PayrollEntry(Document): Get loan details from submitted salary slip based on selected criteria """ cond = self.get_filter_condition() - return frappe.db.sql(""" select eld.employee_loan_account, + return frappe.db.sql(""" select eld.employee_loan_account, eld.employee_loan, eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment from `tabSalary Slip` t1, `tabSalary Slip Loan` eld @@ -283,7 +283,12 @@ class PayrollEntry(Document): "account": data.employee_loan_account, "credit_in_account_currency": data.principal_amount }) - accounts.append({ + + if data.interest_amount and not data.interest_income_account: + frappe.throw(_("Select interest income account in employee loan {0}").format(data.employee_loan)) + + if data.interest_income_account and data.interest_amount: + accounts.append({ "account": data.interest_income_account, "credit_in_account_currency": data.interest_amount, "cost_center": self.cost_center, diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index da2598ff93..7b36b5116d 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -156,9 +156,10 @@ class SalarySlip(TransactionBase): }) def get_date_details(self): - date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date) - self.start_date = date_details.start_date - self.end_date = date_details.end_date + if not self.end_date: + date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date) + self.start_date = date_details.start_date + self.end_date = date_details.end_date def check_sal_struct(self, joining_date, relieving_date): cond = '' diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 47853127b8..c6463fa37d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -51,7 +51,6 @@ class BOM(WebsiteGenerator): self.validate_currency() self.set_conversion_rate() self.validate_uom_is_interger() - self.update_stock_qty() self.set_bom_material_details() self.validate_materials() self.validate_operations() @@ -251,14 +250,12 @@ class BOM(WebsiteGenerator): set_default(self, "item") item = frappe.get_doc("Item", self.item) if item.default_bom != self.name: - item.default_bom = self.name - item.save(ignore_permissions = True) + frappe.db.set_value('Item', self.item, 'default_bom', self.name) else: frappe.db.set(self, "is_default", 0) item = frappe.get_doc("Item", self.item) if item.default_bom == self.name: - item.default_bom = None - item.save(ignore_permissions = True) + frappe.db.set_value('Item', self.item, 'default_bom', None) def clear_operations(self): if not self.with_operations: @@ -295,6 +292,8 @@ class BOM(WebsiteGenerator): m.uom = m.stock_uom m.qty = m.stock_qty + m.db_update() + def validate_uom_is_interger(self): from erpnext.utilities.transaction_base import validate_uom_is_integer validate_uom_is_integer(self, "uom", "qty", "BOM Item") @@ -337,19 +336,23 @@ class BOM(WebsiteGenerator): def check_recursion(self): """ Check whether recursion occurs in any bom""" + bom_list = self.traverse_tree() + bom_nos = frappe.get_all('BOM Item', fields=["bom_no"], + filters={'parent': ('in', bom_list), 'parenttype': 'BOM'}) - check_list = [['parent', 'bom_no', 'parent'], ['bom_no', 'parent', 'child']] - for d in check_list: - bom_list, count = [self.name], 0 - while (len(bom_list) > count ): - boms = frappe.db.sql(" select %s from `tabBOM Item` where %s = %s and parenttype='BOM'" % - (d[0], d[1], '%s'), cstr(bom_list[count])) - count = count + 1 - for b in boms: - if b[0] == self.name: - frappe.throw(_("BOM recursion: {0} cannot be parent or child of {2}").format(b[0], self.name)) - if b[0]: - bom_list.append(b[0]) + raise_exception = False + if bom_nos and self.name in [d.bom_no for d in bom_nos]: + raise_exception = True + + if not raise_exception: + bom_nos = frappe.get_all('BOM Item', fields=["parent"], + filters={'bom_no': self.name, 'parenttype': 'BOM'}) + + if self.name in [d.parent for d in bom_nos]: + raise_exception = True + + if raise_exception: + frappe.throw(_("BOM recursion: {0} cannot be parent or child of {2}").format(self.name, self.name)) def update_cost_and_exploded_items(self, bom_list=[]): bom_list = self.traverse_tree(bom_list) diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js index 4ec9bef998..185b9ed4bc 100644 --- a/erpnext/manufacturing/doctype/bom/bom_tree.js +++ b/erpnext/manufacturing/doctype/bom/bom_tree.js @@ -60,5 +60,14 @@ frappe.treeview_settings["BOM"] = { condition: 'frappe.boot.user.can_create.indexOf("BOM") !== -1' } ], + onrender: function(node) { + if(node.is_root && node.data.value!="BOM") { + frappe.model.with_doc("BOM", node.data.value, function() { + var bom = frappe.model.get_doc("BOM", node.data.value); + node.data.image = bom.image || ""; + node.data.description = bom.description || ""; + }); + } + }, view_template: 'bom_item_preview' } \ No newline at end of file diff --git a/erpnext/non_profit/doctype/chapter/templates/chapter.html b/erpnext/non_profit/doctype/chapter/templates/chapter.html index 04a715886b..0110950cb9 100644 --- a/erpnext/non_profit/doctype/chapter/templates/chapter.html +++ b/erpnext/non_profit/doctype/chapter/templates/chapter.html @@ -46,17 +46,18 @@

Chapter Head

+ {% set doc = frappe.get_doc('Member',chapter_head) %} - + - + - +
Name{{ frappe.db.get_value('User', chapter_head, 'full_name') }}{{ doc.member_name }}
Email{{ frappe.db.get_value('User', chapter_head, 'email') or '' }}{{ frappe.db.get_value('User', doc.email, 'email') or '' }}
Phone{{ frappe.db.get_value('User', chapter_head, 'phone') or '' }}{{ frappe.db.get_value('User', doc.email, 'phone') or '' }}

Address

diff --git a/erpnext/non_profit/doctype/chapter_member/chapter_member.json b/erpnext/non_profit/doctype/chapter_member/chapter_member.json index 0b8f5a77c4..478bfd9331 100644 --- a/erpnext/non_profit/doctype/chapter_member/chapter_member.json +++ b/erpnext/non_profit/doctype/chapter_member/chapter_member.json @@ -41,6 +41,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -71,6 +72,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -101,6 +103,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -108,7 +111,8 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "columns": 0, + "columns": 2, + "default": "1", "fieldname": "enabled", "fieldtype": "Check", "hidden": 0, @@ -116,7 +120,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Enabled", "length": 0, @@ -131,6 +135,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -161,6 +166,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -174,7 +180,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-01-12 12:16:10.591039", + "modified": "2018-03-07 05:36:51.664816", "modified_by": "Administrator", "module": "Non Profit", "name": "Chapter Member", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index fd18680477..e049a81d29 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -504,4 +504,8 @@ execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing= erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group erpnext.patches.v10_0.add_default_cash_flow_mappers erpnext.patches.v11_0.make_quality_inspection_template -# erpnext.patches.v10_0.update_territory_and_customer_group \ No newline at end of file +erpnext.patches.v10_0.remove_and_copy_fields_in_physician +erpnext.patches.v10_0.update_status_for_multiple_source_in_po +erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry +erpnext.patches.v10_0.update_territory_and_customer_group +erpnext.patches.v10_0.update_warehouse_address_details diff --git a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py index e925952c48..c939dd5d33 100644 --- a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py +++ b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py @@ -7,7 +7,7 @@ import frappe def execute(): - items_barcode = frappe.get_list('Item', ['name', 'barcode'], { 'barcode': ('!=', '') }) + items_barcode = frappe.get_all('Item', ['name', 'barcode'], { 'barcode': ('!=', '') }) frappe.reload_doc("stock", "doctype", "item") frappe.reload_doc("stock", "doctype", "item_barcode") diff --git a/erpnext/patches/v10_0/remove_and_copy_fields_in_physician.py b/erpnext/patches/v10_0/remove_and_copy_fields_in_physician.py new file mode 100644 index 0000000000..bf286449c4 --- /dev/null +++ b/erpnext/patches/v10_0/remove_and_copy_fields_in_physician.py @@ -0,0 +1,12 @@ +import frappe + +def execute(): + if frappe.db.exists("DocType", "Physician"): + frappe.reload_doc("healthcare", "doctype", "physician") + frappe.reload_doc("healthcare", "doctype", "physician_service_unit_schedule") + if frappe.db.has_column('Physician', 'physician_schedule'): + for doc in frappe.get_all('Physician'): + _doc = frappe.get_doc('Physician', doc.name) + if _doc.physician_schedule: + _doc.append('physician_schedules', {'schedule': _doc.physician_schedule}) + _doc.save() diff --git a/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py b/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py new file mode 100644 index 0000000000..c6470f21d7 --- /dev/null +++ b/erpnext/patches/v10_0/set_auto_created_serial_no_in_stock_entry.py @@ -0,0 +1,56 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + serialised_items = [d.name for d in frappe.get_all("Item", filters={"has_serial_no": 1})] + + if not serialised_items: + return + + for dt in ["Stock Entry Detail", "Purchase Receipt Item", "Purchase Invoice Item"]: + cond = "" + if dt=="Purchase Invoice Item": + cond = """ and parent in (select name from `tabPurchase Invoice` + where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.parent and update_stock=1)""" + + item_rows = frappe.db.sql(""" + select name + from `tab{0}` + where conversion_factor != 1 + and docstatus = 1 + and ifnull(serial_no, '') = '' + and item_code in ({1}) + {2} + """.format(dt, ', '.join(['%s']*len(serialised_items)), cond), tuple(serialised_items)) + + if item_rows: + sle_serial_nos = dict(frappe.db.sql(""" + select voucher_detail_no, serial_no + from `tabStock Ledger Entry` + where ifnull(serial_no, '') != '' + and voucher_detail_no in (%s) + """.format(', '.join(['%s']*len(item_rows))), + tuple([d[0] for d in item_rows]))) + + batch_size = 100 + for i in range(0, len(item_rows), batch_size): + batch_item_rows = item_rows[i:i + batch_size] + when_then = [] + for item_row in batch_item_rows: + + when_then.append('WHEN `name` = "{row_name}" THEN "{value}"'.format( + row_name=item_row[0], + value=sle_serial_nos.get(item_row[0]))) + + frappe.db.sql(""" + update + `tab{doctype}` + set + serial_no = CASE {when_then_cond} ELSE `serial_no` END + """.format( + doctype = dt, + when_then_cond=" ".join(when_then) + )) \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py b/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py new file mode 100644 index 0000000000..fd3be08b89 --- /dev/null +++ b/erpnext/patches/v10_0/update_status_for_multiple_source_in_po.py @@ -0,0 +1,40 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + + + # update the sales order item in the material request + frappe.reload_doc('stock', 'doctype', 'material_request_item') + frappe.db.sql('''update `tabMaterial Request Item` mri, `tabSales Order Item` soi + set mri.sales_order_item = soi.name + where ifnull(mri.sales_order, "")!="" and soi.parent=mri.sales_order + and soi.item_code=mri.item_code and mri.docstatus=1 + ''') + + # update the sales order item in the purchase order + frappe.db.sql('''update `tabPurchase Order Item` poi, `tabSales Order Item` soi + set poi.sales_order_item = soi.name + where ifnull(poi.sales_order, "")!="" and soi.parent=poi.sales_order + and soi.item_code=poi.item_code and poi.docstatus = 1 + ''') + + # Update the status in material request and sales order + po_list = frappe.db.sql(''' + select parent from `tabPurchase Order Item` where ifnull(material_request, "")!="" and + ifnull(sales_order, "")!="" and docstatus=1 + ''',as_dict=1) + + for po in list(set([d.get("parent") for d in po_list if d.get("parent")])): + try: + po_doc = frappe.get_doc("Purchase Order", po) + + # update the so in the status updater + po_doc.update_status_updater() + po_doc.update_qty(update_modified=False) + + except Exception: + pass diff --git a/erpnext/patches/v10_0/update_territory_and_customer_group.py b/erpnext/patches/v10_0/update_territory_and_customer_group.py index 3889c737e0..c02d3276aa 100644 --- a/erpnext/patches/v10_0/update_territory_and_customer_group.py +++ b/erpnext/patches/v10_0/update_territory_and_customer_group.py @@ -3,34 +3,26 @@ from frappe.model.rename_doc import get_fetch_fields def execute(): ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"] - customers = frappe.get_all('Customer', fields=["name", "territory", "customer_group"]) - - territory_fetch = get_fetch_fields('Customer', 'Territory', ignore_doctypes) + customers = frappe.get_all('Customer', fields=["name", "customer_group"]) customer_group_fetch = get_fetch_fields('Customer', 'Customer Group', ignore_doctypes) batch_size = 1000 for i in range(0, len(customers), batch_size): batch_customers = customers[i:i + batch_size] - for source_fieldname, linked_doctypes_info in [["customer_group", customer_group_fetch], ["territory", territory_fetch]]: - for d in linked_doctypes_info: - when_then = [] - for customer in batch_customers: - when_then.append(''' - WHEN `{master_fieldname}` = "{docname}" and {linked_to_fieldname} != "{value}" - THEN "{value}" - '''.format( - master_fieldname=d["master_fieldname"], - linked_to_fieldname=d["linked_to_fieldname"], - docname=frappe.db.escape(customer.name).encode("utf-8"), - value=frappe.db.escape(customer.get(source_fieldname)).encode("utf-8"))) - - frappe.db.sql(""" - update - `tab{doctype}` - set - {linked_to_fieldname} = CASE {when_then_cond} ELSE `{linked_to_fieldname}` END - """.format( - doctype = d['doctype'], - when_then_cond=" ".join(when_then), - linked_to_fieldname=d.linked_to_fieldname - )) + for d in customer_group_fetch: + when_then = [] + for customer in batch_customers: + value = frappe.db.escape(frappe.as_unicode(customer.get("customer_group"))) + + when_then.append(''' + WHEN `%s` = "%s" and %s != "%s" + THEN "%s" + '''%(d["master_fieldname"], frappe.db.escape(frappe.as_unicode(customer.name)), + d["linked_to_fieldname"], value, value)) + + frappe.db.sql(""" + update + `tab%s` + set + %s = CASE %s ELSE `%s` END + """%(d['doctype'], d.linked_to_fieldname, " ".join(when_then), d.linked_to_fieldname)) diff --git a/erpnext/patches/v10_0/update_warehouse_address_details.py b/erpnext/patches/v10_0/update_warehouse_address_details.py new file mode 100644 index 0000000000..b982b9a662 --- /dev/null +++ b/erpnext/patches/v10_0/update_warehouse_address_details.py @@ -0,0 +1,37 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + warehouse = frappe.db.sql("""select name, email_id, phone_no, mobile_no, address_line_1, + address_line_2, city, state, pin from `tabWarehouse` where ifnull(address_line_1, '') != '' + or ifnull(mobile_no, '') != '' + or ifnull(email_id, '') != '' """, as_dict=1) + + for d in warehouse: + try: + address = frappe.new_doc('Address') + address.name = d.name + address.address_title = d.name + address.address_line1 = d.address_line_1 + address.city = d.city + address.state = d.state + address.pincode = d.pin + address.db_insert() + address.append('links',{'link_doctype':'Warehouse','link_name':d.name}) + address.links[0].db_insert() + if d.name and (d.email_id or d.mobile_no or d.phone_no): + contact = frappe.new_doc('Contact') + contact.name = d.name + contact.first_name = d.name + contact.mobile_no = d.mobile_no + contact.email_id = d.email_id + contact.phone = d.phone_no + contact.db_insert() + contact.append('links',{'link_doctype':'Warehouse','link_name':d.name}) + contact.links[0].db_insert() + except frappe.DuplicateEntryError: + pass + \ No newline at end of file diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 76c34db1e9..e418845f3f 100644 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -69,6 +69,7 @@ class Task(NestedSet): self.reschedule_dependent_tasks() self.update_project() self.unassign_todo() + self.populate_depends_on() def unassign_todo(self): if self.status == "Closed" or self.status == "Cancelled": @@ -137,6 +138,16 @@ class Task(NestedSet): if project_user: return True + def populate_depends_on(self): + if self.parent_task: + parent = frappe.get_doc('Task', self.parent_task) + parent.append("depends_on", { + "doctype": "Task Depends On", + "task": self.name, + "subject": self.subject + }) + parent.save() + def on_trash(self): if check_if_child_exists(self.name): throw(_("Child Task exists for this Task. You can not delete this Task.")) @@ -216,12 +227,12 @@ def add_node(): @frappe.whitelist() def add_multiple_tasks(data, parent): - data = json.loads(data)['tasks'] - tasks = data.split('\n') - new_doc = {'doctype': 'Task', 'parent_task': parent} - new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') + data = json.loads(data) + new_doc = {'doctype': 'Task', 'parent_task': parent if parent!="All Tasks" else ""} + new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') or "" - for d in tasks: - new_doc['subject'] = d + for d in data: + if not d.get("subject"): continue + new_doc['subject'] = d.get("subject") new_task = frappe.get_doc(new_doc) new_task.insert() diff --git a/erpnext/projects/doctype/task/task_tree.js b/erpnext/projects/doctype/task/task_tree.js index 3ff00f6fc5..d1d872f28a 100644 --- a/erpnext/projects/doctype/task/task_tree.js +++ b/erpnext/projects/doctype/task/task_tree.js @@ -44,23 +44,39 @@ frappe.treeview_settings['Task'] = { return node.expandable; }, click: function(node) { - var d = new frappe.ui.Dialog({ - 'fields': [ - {'fieldname': 'tasks', 'label': 'Tasks', 'fieldtype': 'Text'}, + this.data = []; + const dialog = new frappe.ui.Dialog({ + title: __("Add Multiple Tasks"), + fields: [ + { + fieldname: "multiple_tasks", fieldtype: "Table", + in_place_edit: true, data: this.data, + get_data: () => { + return this.data; + }, + fields: [{ + fieldtype:'Data', + fieldname:"subject", + in_list_view: 1, + reqd: 1, + label: __("Subject") + }] + }, ], primary_action: function() { - d.hide(); + dialog.hide(); return frappe.call({ method: "erpnext.projects.doctype.task.task.add_multiple_tasks", args: { - data: d.get_values(), + data: dialog.get_values()["multiple_tasks"], parent: node.data.value }, callback: function() { } }); - } + }, + primary_action_label: __('Create') }); - d.show(); + dialog.show(); } } ], diff --git a/erpnext/public/images/YouTube-icon-full_color.png b/erpnext/public/images/YouTube-icon-full_color.png index 5322e65a92..d3403841ee 100644 Binary files a/erpnext/public/images/YouTube-icon-full_color.png and b/erpnext/public/images/YouTube-icon-full_color.png differ diff --git a/erpnext/public/images/erp-icon.svg b/erpnext/public/images/erp-icon.svg index b4e2a24b4e..387e94dda4 100644 --- a/erpnext/public/images/erp-icon.svg +++ b/erpnext/public/images/erp-icon.svg @@ -1,46 +1,80 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +image/svg+xml \ No newline at end of file diff --git a/erpnext/public/images/erpnext-footer.png b/erpnext/public/images/erpnext-footer.png index 4b36fa120c..ffff7756b7 100644 Binary files a/erpnext/public/images/erpnext-footer.png and b/erpnext/public/images/erpnext-footer.png differ diff --git a/erpnext/public/images/erpnext-grey.png b/erpnext/public/images/erpnext-grey.png deleted file mode 100644 index 0e70f0538b..0000000000 Binary files a/erpnext/public/images/erpnext-grey.png and /dev/null differ diff --git a/erpnext/public/images/erpnext-logo.jpg b/erpnext/public/images/erpnext-logo.jpg index 811eca833e..b5acc5af1a 100644 Binary files a/erpnext/public/images/erpnext-logo.jpg and b/erpnext/public/images/erpnext-logo.jpg differ diff --git a/erpnext/public/images/favicon.png b/erpnext/public/images/favicon.png index 62c328536a..b6948856f8 100644 Binary files a/erpnext/public/images/favicon.png and b/erpnext/public/images/favicon.png differ diff --git a/erpnext/public/images/splash.png b/erpnext/public/images/splash.png index 45255937ae..8e5d055c66 100644 Binary files a/erpnext/public/images/splash.png and b/erpnext/public/images/splash.png differ diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index fc950c6f51..ccf4082a4b 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -54,16 +54,14 @@ class Customer(TransactionBase): self.flags.old_lead = self.lead_name validate_party_accounts(self) self.validate_credit_limit_on_change() - self.check_customer_group_or_territory_change() + self.check_customer_group_change() - def check_customer_group_or_territory_change(self): - frappe.flags.customer_group_changed, frappe.flags.territory_changed = False, False + def check_customer_group_change(self): + frappe.flags.customer_group_changed = False if not self.get('__islocal'): if self.customer_group != frappe.db.get_value('Customer', self.name, 'customer_group'): frappe.flags.customer_group_changed = True - if self.territory != frappe.db.get_value('Customer', self.name, 'territory'): - frappe.flags.territory_changed = True def on_update(self): self.validate_name_with_customer_group() @@ -76,12 +74,10 @@ class Customer(TransactionBase): if self.flags.is_new_doc: self.create_lead_address_contact() - self.update_territory_and_customer_groups() + self.update_customer_groups() - def update_territory_and_customer_groups(self): + def update_customer_groups(self): ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"] - if frappe.flags.territory_changed: - update_linked_doctypes('Customer', self.name, 'Territory', self.territory, ignore_doctypes) if frappe.flags.customer_group_changed: update_linked_doctypes('Customer', self.name, 'Customer Group', self.customer_group, ignore_doctypes) diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 9bd6d4c964..92db12be91 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -41,7 +41,7 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ var me = this; - if (doc.__islocal) { + if (doc.__islocal && !doc.valid_till) { if(frappe.boot.sysdefaults.quotation_valid_till){ this.frm.set_value('valid_till', frappe.datetime.add_days(doc.transaction_date, frappe.boot.sysdefaults.quotation_valid_till)); } else { diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 1c89d467b6..4208b08d87 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -435,6 +435,7 @@ def make_material_request(source_name, target_doc=None): "Sales Order Item": { "doctype": "Material Request Item", "field_map": { + "name": "sales_order_item", "parent": "sales_order", "stock_uom": "uom", "stock_qty": "qty" diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index fccebd4991..a027a98656 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -259,12 +259,16 @@ erpnext.pos.PointOfSale = class PointOfSale { frappe.msgprint(__("Quantity must be positive")); value = item.qty; } else { - item[field] = value; - if (field == "serial_no" && value) { - let serial_nos = value.split("\n"); - item["qty"] = serial_nos.filter(d => { - return d!==""; - }).length; + if (in_list(["qty", "serial_no", "batch"], field)) { + item[field] = value; + if (field == "serial_no" && value) { + let serial_nos = value.split("\n"); + item["qty"] = serial_nos.filter(d => { + return d!==""; + }).length; + } + } else { + return frappe.model.set_value(item.doctype, item.name, field, value); } } @@ -1504,7 +1508,7 @@ class Payment { fieldname: p.mode_of_payment, default: p.amount, onchange: () => { - const value = this.dialog.get_value(this.fieldname); + const value = this.dialog.get_value(this.fieldname) || 0; me.update_payment_value(this.fieldname, value); } }; diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 14a478d7e4..0ff7c2b3aa 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -102,7 +102,7 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non or I.name like %(search)s)""" search = "%" + cstr(search) + "%" - query += """order by I.weightage desc, in_stock desc, I.item_name limit %s, %s""" % (start, limit) + query += """order by I.weightage desc, in_stock desc, I.modified desc limit %s, %s""" % (start, limit) data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index 0e4ba6bc92..19318df38e 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -1230,14 +1230,18 @@ }, "Switzerland": { - "Switzerland VAT 8%": { - "account_name": "VAT 8%", - "tax_rate": 8.00, + "Switzerland normal VAT": { + "account_name": "VAT 7.7%", + "tax_rate": 7.70, "default": 1 }, - "Switzerland VAT 2.4%": { - "account_name": "VAT 2.4%", - "tax_rate": 2.40 + "Switzerland reduced VAT": { + "account_name": "VAT 2.5%", + "tax_rate": 2.50 + }, + "Switzerland lodging VAT": { + "account_name": "VAT 3.7%", + "tax_rate": 3.70 } }, diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index ea705cb00d..ff01a19be4 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -427,7 +427,8 @@ $.extend(erpnext.item, { ["parent","=", attribute] ], fields: ["attribute_value"], - limit_page_length: null + limit_start: 0, + limit_page_length: 500 } }).then((r) => { if(r.message) { diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index e6e933c563..9834183d77 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -261,7 +261,8 @@ def make_purchase_order(source_name, target_doc=None): ["parent", "material_request"], ["uom", "stock_uom"], ["uom", "uom"], - ["sales_order", "sales_order"] + ["sales_order", "sales_order"], + ["sales_order_item", "sales_order_item"] ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 8c63ae7255..d31d9e7b71 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -670,6 +670,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sales_order_item", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Sales Order Item", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 1, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -959,8 +989,8 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-12-20 16:29:18.902085", - "modified_by": "nabinhait@gmail.com", + "modified": "2018-02-12 05:51:39.954530", + "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", "owner": "Administrator", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index fbbdeb61db..d9f40cca90 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -11,7 +11,7 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchas from erpnext import set_perpetual_inventory from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError from erpnext.accounts.doctype.account.test_account import get_inventory_account - +from erpnext.stock.doctype.item.test_item import make_item class TestPurchaseReceipt(unittest.TestCase): def setUp(self): @@ -203,6 +203,22 @@ class TestPurchaseReceipt(unittest.TestCase): "delivery_document_no": return_pr.name }) + def test_purchase_return_for_multi_uom(self): + item_code = "_Test Purchase Return For Multi-UOM" + if not frappe.db.exists('Item', item_code): + item = make_item(item_code, {'stock_uom': 'Box'}) + row = item.append('uoms', { + 'uom': 'Unit', + 'conversion_factor': 0.1 + }) + row.db_update() + + pr = make_purchase_receipt(item_code=item_code, qty=1, uom="Box", conversion_factor=1.0) + return_pr = make_purchase_receipt(item_code=item_code, qty=-10, uom="Unit", + stock_uom="Box", conversion_factor=0.1, is_return=1, return_against=pr.name) + + self.assertEquals(abs(return_pr.items[0].stock_qty), 1.0) + def test_closed_purchase_receipt(self): from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_purchase_receipt_status @@ -255,7 +271,6 @@ class TestPurchaseReceipt(unittest.TestCase): def test_not_accept_duplicate_serial_no(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry - from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note item_code = frappe.db.get_value('Item', {'has_serial_no': 1}) @@ -307,9 +322,10 @@ def make_purchase_receipt(**args): "rejected_qty": rejected_qty, "rejected_warehouse": args.rejected_warehouse or "_Test Rejected Warehouse - _TC" if rejected_qty != 0 else "", "rate": args.rate or 50, - "conversion_factor": 1.0, + "conversion_factor": args.conversion_factor or 1.0, "serial_no": args.serial_no, - "stock_uom": "_Test UOM" + "stock_uom": args.stock_uom or "_Test UOM", + "uom": args.uom or "_Test UOM" }) if not args.do_not_save: diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 1b4cfd9e49..754bd71879 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -40,6 +40,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -69,6 +70,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -102,6 +104,7 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -133,6 +136,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -162,6 +166,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -193,6 +198,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -223,6 +229,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -255,6 +262,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "300px" }, @@ -284,6 +292,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -314,6 +323,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -345,6 +355,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -374,6 +385,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -406,6 +418,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -439,6 +452,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -472,6 +486,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -502,6 +517,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -536,6 +552,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -570,6 +587,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -604,6 +622,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -636,6 +655,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -668,6 +688,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -697,6 +718,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -727,6 +749,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -757,6 +780,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -785,6 +809,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -815,6 +840,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -843,6 +869,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -876,6 +903,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -909,6 +937,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -937,6 +966,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -970,6 +1000,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -1004,6 +1035,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -1035,6 +1067,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1064,6 +1097,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1095,6 +1129,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1126,6 +1161,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1155,6 +1191,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1186,6 +1223,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1217,6 +1255,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1247,6 +1286,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1277,6 +1317,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1307,6 +1348,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1336,6 +1378,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1367,6 +1410,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1396,6 +1440,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1429,6 +1474,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -1463,6 +1509,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -1497,6 +1544,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1526,6 +1574,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1559,6 +1608,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -1591,6 +1641,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1623,6 +1674,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, @@ -1653,6 +1705,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1684,6 +1737,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1716,6 +1770,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1746,6 +1801,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1775,6 +1831,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1804,6 +1861,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1834,6 +1892,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1866,6 +1925,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1898,6 +1958,7 @@ "reqd": 0, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -1927,6 +1988,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1957,6 +2019,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -1988,6 +2051,39 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:parent.is_subcontracted == 'Yes'", + "fieldname": "include_exploded_items", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Include Exploded Items", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2018,6 +2114,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2047,6 +2144,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2079,6 +2177,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2112,6 +2211,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2145,6 +2245,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -2179,6 +2280,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, @@ -2213,6 +2315,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "80px" }, @@ -2246,6 +2349,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -2277,6 +2381,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -2290,7 +2395,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-12-06 13:50:08.201145", + "modified": "2018-02-22 15:15:38.793425", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 0520a8d3f0..3322d54558 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -191,7 +191,7 @@ def validate_serial_no(sle, item_det): if sle.serial_no: frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code), SerialNoNotRequiredError) - else: + elif sle.is_cancelled == "No": if sle.serial_no: serial_nos = get_serial_nos(sle.serial_no) if cint(sle.actual_qty) != flt(sle.actual_qty): @@ -326,11 +326,16 @@ def update_serial_nos_after_submit(controller, parentfield): update_rejected_serial_nos = True if (controller.doctype in ("Purchase Receipt", "Purchase Invoice") and d.rejected_qty) else False accepted_serial_nos_updated = False - warehouse = d.t_warehouse if controller.doctype == "Stock Entry" else d.warehouse + if controller.doctype == "Stock Entry": + warehouse = d.t_warehouse + qty = d.transfer_qty + else: + warehouse = d.warehouse + qty = d.stock_qty for sle in stock_ledger_entries: if sle.voucher_detail_no==d.name: - if not accepted_serial_nos_updated and d.qty and abs(sle.actual_qty)==d.qty \ + if not accepted_serial_nos_updated and qty and abs(sle.actual_qty)==qty \ and sle.warehouse == warehouse and sle.serial_no != d.serial_no: d.serial_no = sle.serial_no frappe.db.set_value(d.doctype, d.name, "serial_no", sle.serial_no) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 825b4cbeed..b4d8e1500b 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -313,6 +313,14 @@ frappe.ui.form.on('Stock Entry', { frm.set_value("total_additional_costs", flt(total_additional_costs, precision("total_additional_costs"))); }, + + source_warehouse_address: function(frm) { + erpnext.utils.get_address_display(frm, 'source_warehouse_address', 'source_address_display', false); + }, + + target_warehouse_address: function(frm) { + erpnext.utils.get_address_display(frm, 'target_warehouse_address', 'target_address_display', false); + }, }) frappe.ui.form.on('Stock Entry Detail', { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 83bf92d8d2..b6ad6dae4e 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -768,6 +768,68 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "from_warehouse", + "fieldname": "source_warehouse_address", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Source Warehouse Address", + "length": 0, + "no_copy": 0, + "options": "Address", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "source_address_display", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Source Warehouse Address", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -828,6 +890,68 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "to_warehouse", + "fieldname": "target_warehouse_address", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Target Warehouse Name", + "length": 0, + "no_copy": 0, + "options": "Address", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "target_address_display", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Target Warehouse Address", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1770,7 +1894,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-02-17 10:32:24.111113", + "modified": "2018-03-01 12:27:12.884611", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js index 5c23a9729a..1bfa416bdf 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.js +++ b/erpnext/stock/doctype/warehouse/warehouse.js @@ -5,6 +5,15 @@ frappe.ui.form.on("Warehouse", { refresh: function(frm) { frm.toggle_display('warehouse_name', frm.doc.__islocal); + frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); + + + if(!frm.doc.__islocal) { + frappe.contacts.render_address_and_contact(frm); + + } else { + frappe.contacts.clear_address_and_contact(frm); + } frm.add_custom_button(__("Stock Balance"), function() { frappe.set_route("query-report", "Stock Balance", {"warehouse": frm.doc.name}); diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index b6307a4633..82d0d0c4fb 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -226,6 +226,125 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "address_and_contact", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Address and Contact", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "address_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Address HTML", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_10", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "contact_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Contact HTML", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -699,7 +818,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-01-23 16:45:45.546649", + "modified": "2018-02-28 09:15:33.463838", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 5186408721..0eee6baddd 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -7,6 +7,7 @@ from frappe.utils import cint, validate_email_add from frappe import throw, _ from frappe.utils.nestedset import NestedSet from erpnext.stock import get_warehouse_account +from frappe.contacts.address_and_contact import load_address_and_contact class Warehouse(NestedSet): nsm_parent_field = 'parent_warehouse' @@ -25,10 +26,8 @@ class Warehouse(NestedSet): if account: self.set_onload('account', account) + load_address_and_contact(self) - def validate(self): - if self.email_id: - validate_email_add(self.email_id, True) def on_update(self): self.update_nsm_model() diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index fdf4442797..e1249ea3cd 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py @@ -24,9 +24,8 @@ def execute(filters=None): data = [] for item in items: - - total_outgoing = consumed_item_map.get(item.name, 0)+delivered_item_map.get(item.name,0) - avg_daily_outgoing = flt(total_outgoing/diff, float_preceision) + total_outgoing = consumed_item_map.get(item.name, 0) + delivered_item_map.get(item.name,0) + avg_daily_outgoing = flt(total_outgoing / diff, float_preceision) reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock) data.append([item.name, item.item_name, item.description, item.safety_stock, item.lead_time_days, @@ -45,12 +44,11 @@ def get_columns(): def get_item_info(): return frappe.db.sql("""select name, item_name, description, safety_stock, - lead_time_days from tabItem""", as_dict=1) + lead_time_days from tabItem""", as_dict=1) def get_consumed_items(condition): - cn_items = frappe.db.sql("""select se_item.item_code, - sum(se_item.actual_qty) as 'consume_qty' + sum(se_item.transfer_qty) as 'consume_qty' from `tabStock Entry` se, `tabStock Entry Detail` se_item where se.name = se_item.parent and se.docstatus = 1 and ifnull(se_item.t_warehouse, '') = '' %s @@ -63,17 +61,16 @@ def get_consumed_items(condition): return cn_items_map def get_delivered_items(condition): - - dn_items = frappe.db.sql("""select dn_item.item_code, sum(dn_item.qty) as dn_qty + dn_items = frappe.db.sql("""select dn_item.item_code, sum(dn_item.stock_qty) as dn_qty from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item where dn.name = dn_item.parent and dn.docstatus = 1 %s group by dn_item.item_code""" % (condition), as_dict=1) - si_items = frappe.db.sql("""select si_item.item_name, sum(si_item.qty) as si_qty + si_items = frappe.db.sql("""select si_item.item_code, sum(si_item.stock_qty) as si_qty from `tabSales Invoice` si, `tabSales Invoice Item` si_item where si.name = si_item.parent and si.docstatus = 1 and - si.update_stock = 1 and si.is_pos = 1 %s - group by si_item.item_name""" % (condition), as_dict=1) + si.update_stock = 1 %s + group by si_item.item_code""" % (condition), as_dict=1) dn_item_map = {} for item in dn_items: diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 86e102982b..e93957d97a 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -96,6 +96,8 @@ def get_item_conditions(filters): def get_sle_conditions(filters): conditions = [] if filters.get("warehouse"): - conditions.append("warehouse=%(warehouse)s") + lft, rgt = frappe.db.get_value('Warehouse', filters.get("warehouse"), ['lft', 'rgt']) + conditions.append("""warehouse in (select wh.name from `tabWarehouse` wh + where wh.lft >= {0} and rgt <= {1})""".format(lft, rgt)) return "and {}".format(" and ".join(conditions)) if conditions else "" diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 4d4435ef82..26b4a24bbd 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -454,22 +454,22 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, where item_code = %s and valuation_rate > 0 order by posting_date desc, posting_time desc, name desc limit 1""", item_code) - valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0 + if last_valuation_rate: + return flt(last_valuation_rate[0][0]) # as there is previous records, it might come with zero rate + + # If negative stock allowed, and item delivered without any incoming entry, + # system does not found any SLE, then take valuation rate from Item + valuation_rate = frappe.db.get_value("Item", item_code, "valuation_rate") if not valuation_rate: - # If negative stock allowed, and item delivered without any incoming entry, - # syste does not found any SLE, then take valuation rate from Item - valuation_rate = frappe.db.get_value("Item", item_code, "valuation_rate") + # try Item Standard rate + valuation_rate = frappe.db.get_value("Item", item_code, "standard_rate") if not valuation_rate: - # try Item Standard rate - valuation_rate = frappe.db.get_value("Item", item_code, "standard_rate") - - if not valuation_rate: - # try in price list - valuation_rate = frappe.db.get_value('Item Price', - dict(item_code=item_code, buying=1, currency=currency), - 'price_list_rate') + # try in price list + valuation_rate = frappe.db.get_value('Item Price', + dict(item_code=item_code, buying=1, currency=currency), + 'price_list_rate') if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \ and cint(erpnext.is_perpetual_inventory_enabled(company)): diff --git a/erpnext/templates/emails/reorder_item.html b/erpnext/templates/emails/reorder_item.html index c1aa897038..05af3167ba 100644 --- a/erpnext/templates/emails/reorder_item.html +++ b/erpnext/templates/emails/reorder_item.html @@ -9,7 +9,7 @@ {{ _("Warehouse") }} {{ _("Quantity") }} {{ _("UOM") }} - {{ _("Balance Qty") }} + {{ _("Projected Qty") }} @@ -22,7 +22,7 @@ {{ item.warehouse }} {{ item.qty }} {{ item.uom }} - {{ item.balance_qty }} + {{ frappe.utils.flt(item.projected_qty) + frappe.utils.flt(item.qty) }} {%- endfor %} diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py index a872f19510..f2fd60024a 100644 --- a/erpnext/templates/pages/product_search.py +++ b/erpnext/templates/pages/product_search.py @@ -35,7 +35,7 @@ def get_product_list(search=None, start=0, limit=12): search = "%" + cstr(search) + "%" # order by - query += """ order by I.weightage desc, in_stock desc, I.item_name limit %s, %s""" % (cint(start), cint(limit)) + query += """ order by I.weightage desc, in_stock desc, I.modified desc limit %s, %s""" % (cint(start), cint(limit)) data = frappe.db.sql(query, { "search": search, diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 6594c301ee..f2327ca53c 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -45,7 +45,7 @@ class TransactionBase(StatusUpdater): events = frappe.db.sql_list("""select name from `tabEvent` where ref_type=%s and ref_name=%s""", (self.doctype, self.name)) if events: - frappe.db.sql("delete from `tabEvent` where name in (%s)" + frappe.db.sql("delete from `tabEvent` where name in ({0})" .format(", ".join(['%s']*len(events))), tuple(events)) def _add_calendar_event(self, opts): @@ -73,6 +73,8 @@ class TransactionBase(StatusUpdater): validate_uom_is_integer(self, uom_field, qty_fields) def validate_with_previous_doc(self, ref): + self.exclude_fields = ["conversion_factor", "uom"] if self.get('is_return') else [] + for key, val in ref.items(): is_child = val.get("is_child_table") ref_doc = {} @@ -103,7 +105,7 @@ class TransactionBase(StatusUpdater): frappe.throw(_("Invalid reference {0} {1}").format(reference_doctype, reference_name)) for field, condition in fields: - if prevdoc_values[field] is not None: + if prevdoc_values[field] is not None and field not in self.exclude_fields: self.validate_value(field, condition, prevdoc_values[field], doc)