diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 0e57b3f198..1adc4c4d2f 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -128,7 +128,8 @@ class Account(NestedSet): "account_currency": self.account_currency, "parent_account": parent_acc_name_map[company] }) - doc.save() + if not self.check_if_child_acc_exists(doc): + doc.save() frappe.msgprint(_("Account {0} is added in the child company {1}") .format(doc.name, company)) @@ -172,6 +173,24 @@ class Account(NestedSet): if frappe.db.get_value("GL Entry", {"account": self.name}): frappe.throw(_("Currency can not be changed after making entries using some other currency")) + def check_if_child_acc_exists(self, doc): + ''' Checks if a account in parent company exists in the ''' + info = frappe.db.get_value("Account", { + "account_name": doc.account_name, + "account_number": doc.account_number + }, ['company', 'account_currency', 'is_group', 'root_type', 'account_type', 'balance_must_be', 'account_name'], as_dict=1) + + if not info: + return + + doc = vars(doc) + dict_diff = [k for k in info if k in doc and info[k] != doc[k] and k != "company"] + if dict_diff: + frappe.throw(_("Account {0} already exists in child company {1}. The following fields have different values, they should be same:") + .format(info.account_name, info.company, '
  • '.join(dict_diff))) + else: + return True + def convert_group_to_ledger(self): if self.check_if_child_exists(): throw(_("Account with child nodes cannot be converted to ledger")) diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json index 8d7ed74eb9..04d6303774 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json @@ -8,7 +8,8 @@ "customer", "column_break_3", "posting_date", - "outstanding_amount" + "outstanding_amount", + "debit_to" ], "fields": [ { @@ -48,10 +49,18 @@ { "fieldname": "column_break_3", "fieldtype": "Column Break" + }, + { + "fetch_from": "sales_invoice.debit_to", + "fieldname": "debit_to", + "fieldtype": "Link", + "label": "Debit to", + "options": "Account", + "read_only": 1 } ], "istable": 1, - "modified": "2019-05-30 19:27:29.436153", + "modified": "2019-08-07 15:13:55.808349", "modified_by": "Administrator", "module": "Accounts", "name": "Discounted Invoice", diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js index 5563f031bb..f1f88a8d64 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js @@ -13,41 +13,57 @@ frappe.ui.form.on('Invoice Discounting', { }; }); - frm.events.filter_accounts("bank_account", frm, {"account_type": "Bank"}); - frm.events.filter_accounts("bank_charges_account", frm, {"root_type": "Expense"}); - frm.events.filter_accounts("short_term_loan", frm, {"root_type": "Liability"}); - frm.events.filter_accounts("accounts_receivable_credit", frm, {"account_type": "Receivable"}); - frm.events.filter_accounts("accounts_receivable_discounted", frm, {"account_type": "Receivable"}); - frm.events.filter_accounts("accounts_receivable_unpaid", frm, {"account_type": "Receivable"}); + + frm.events.filter_accounts("bank_account", frm, [["account_type", "=", "Bank"]]); + frm.events.filter_accounts("bank_charges_account", frm, [["root_type", "=", "Expense"]]); + frm.events.filter_accounts("short_term_loan", frm, [["root_type", "=", "Liability"]]); + frm.events.filter_accounts("accounts_receivable_discounted", frm, [["account_type", "=", "Receivable"]]); + frm.events.filter_accounts("accounts_receivable_credit", frm, [["account_type", "=", "Receivable"]]); + frm.events.filter_accounts("accounts_receivable_unpaid", frm, [["account_type", "=", "Receivable"]]); }, filter_accounts: (fieldname, frm, addl_filters) => { - let filters = { - "company": frm.doc.company, - "is_group": 0 - }; - if(addl_filters) Object.assign(filters, addl_filters); + let filters = [ + ["company", "=", frm.doc.company], + ["is_group", "=", 0] + ]; + if(addl_filters){ + filters = $.merge(filters , addl_filters); + } frm.set_query(fieldname, () => { return { "filters": filters }; }); }, + refresh_filters: (frm) =>{ + let invoice_accounts = Object.keys(frm.doc.invoices).map(function(key) { + return frm.doc.invoices[key].debit_to; + }); + let filters = [ + ["account_type", "=", "Receivable"], + ["name", "not in", invoice_accounts] + ]; + frm.events.filter_accounts("accounts_receivable_credit", frm, filters); + frm.events.filter_accounts("accounts_receivable_discounted", frm, filters); + frm.events.filter_accounts("accounts_receivable_unpaid", frm, filters); + }, + refresh: (frm) => { frm.events.show_general_ledger(frm); - if(frm.doc.docstatus === 0) { + if (frm.doc.docstatus === 0) { frm.add_custom_button(__('Get Invoices'), function() { frm.events.get_invoices(frm); }); } - if(frm.doc.docstatus === 1 && frm.doc.status !== "Settled") { - if(frm.doc.status == "Sanctioned") { + if (frm.doc.docstatus === 1 && frm.doc.status !== "Settled") { + if (frm.doc.status == "Sanctioned") { frm.add_custom_button(__('Disburse Loan'), function() { frm.events.create_disbursement_entry(frm); }).addClass("btn-primary"); } - if(frm.doc.status == "Disbursed") { + if (frm.doc.status == "Disbursed") { frm.add_custom_button(__('Close Loan'), function() { frm.events.close_loan(frm); }).addClass("btn-primary"); @@ -64,7 +80,7 @@ frappe.ui.form.on('Invoice Discounting', { }, set_end_date: (frm) => { - if(frm.doc.loan_start_date && frm.doc.loan_period) { + if (frm.doc.loan_start_date && frm.doc.loan_period) { let end_date = frappe.datetime.add_days(frm.doc.loan_start_date, frm.doc.loan_period); frm.set_value("loan_end_date", end_date); } @@ -132,6 +148,7 @@ frappe.ui.form.on('Invoice Discounting', { frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice); let row = frm.add_child("invoices"); $.extend(row, v); + frm.events.refresh_filters(frm); }); refresh_field("invoices"); } @@ -190,8 +207,10 @@ frappe.ui.form.on('Invoice Discounting', { frappe.ui.form.on('Discounted Invoice', { sales_invoice: (frm) => { frm.events.calculate_total_amount(frm); + frm.events.refresh_filters(frm); }, invoices_remove: (frm) => { frm.events.calculate_total_amount(frm); + frm.events.refresh_filters(frm); } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 29475d5644..36c29113ea 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -12,6 +12,7 @@ from erpnext.accounts.general_ledger import make_gl_entries class InvoiceDiscounting(AccountsController): def validate(self): self.validate_mandatory() + self.validate_invoices() self.calculate_total_amount() self.set_status() self.set_end_date() @@ -24,6 +25,15 @@ class InvoiceDiscounting(AccountsController): if self.docstatus == 1 and not (self.loan_start_date and self.loan_period): frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting")) + def validate_invoices(self): + discounted_invoices = [record.sales_invoice for record in + frappe.get_all("Discounted Invoice",fields = ["sales_invoice"], filters= {"docstatus":1})] + + for record in self.invoices: + if record.sales_invoice in discounted_invoices: + frappe.throw("Row({0}): {1} is already discounted in {2}" + .format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent))) + def calculate_total_amount(self): self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices]) @@ -212,7 +222,8 @@ def get_invoices(filters): name as sales_invoice, customer, posting_date, - outstanding_amount + outstanding_amount, + debit_to from `tabSales Invoice` si where docstatus = 1 diff --git a/erpnext/accounts/doctype/payment_order/regional/india.js b/erpnext/accounts/doctype/payment_order/regional/india.js deleted file mode 100644 index 66d0f60c61..0000000000 --- a/erpnext/accounts/doctype/payment_order/regional/india.js +++ /dev/null @@ -1,29 +0,0 @@ -frappe.ui.form.on('Payment Order', { - refresh: function(frm) { - if (frm.doc.docstatus==1 && frm.doc.payment_order_type==='Payment Entry') { - frm.add_custom_button(__('Generate Text File'), function() { - frm.trigger("generate_text_and_download_file"); - }); - } - }, - generate_text_and_download_file: (frm) => { - return frappe.call({ - method: "erpnext.regional.india.bank_remittance.generate_report", - args: { - name: frm.doc.name - }, - freeze: true, - callback: function(r) { - { - frm.reload_doc(); - const a = document.createElement('a'); - let file_obj = r.message; - a.href = file_obj.file_url; - a.target = '_blank'; - a.download = file_obj.file_name; - a.click(); - } - } - }); - } -}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 479548c6e3..e290b235a9 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -432,7 +432,6 @@ def get_customer_id(doc, customer=None): return cust_id - def make_customer_and_address(customers): customers_list = [] for customer, data in iteritems(customers): @@ -449,7 +448,6 @@ def make_customer_and_address(customers): frappe.db.commit() return customers_list - def add_customer(data): customer = data.get('full_name') or data.get('customer') if frappe.db.exists("Customer", customer.strip()): @@ -466,21 +464,18 @@ def add_customer(data): frappe.db.commit() return customer_doc.name - def get_territory(data): if data.get('territory'): return data.get('territory') return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories') - def get_customer_group(data): if data.get('customer_group'): return data.get('customer_group') return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name') - def make_contact(args, customer): if args.get('email_id') or args.get('phone'): name = frappe.db.get_value('Dynamic Link', @@ -506,7 +501,6 @@ def make_contact(args, customer): doc.flags.ignore_mandatory = True doc.save(ignore_permissions=True) - def make_address(args, customer): if not args.get('address_line1'): return @@ -521,7 +515,10 @@ def make_address(args, customer): address = frappe.get_doc('Address', name) else: address = frappe.new_doc('Address') - address.country = frappe.get_cached_value('Company', args.get('company'), 'country') + if args.get('company'): + address.country = frappe.get_cached_value('Company', + args.get('company'), 'country') + address.append('links', { 'link_doctype': 'Customer', 'link_name': customer @@ -533,7 +530,6 @@ def make_address(args, customer): address.flags.ignore_mandatory = True address.save(ignore_permissions=True) - def make_email_queue(email_queue): name_list = [] for key, data in iteritems(email_queue): @@ -550,7 +546,6 @@ def make_email_queue(email_queue): return name_list - def validate_item(doc): for item in doc.get('items'): if not frappe.db.exists('Item', item.get('item_code')): @@ -569,7 +564,6 @@ def validate_item(doc): item_doc.save(ignore_permissions=True) frappe.db.commit() - def submit_invoice(si_doc, name, doc, name_list): try: si_doc.insert() @@ -585,7 +579,6 @@ def submit_invoice(si_doc, name, doc, name_list): return name_list - def save_invoice(doc, name, name_list): try: if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 4f80b78c88..95c5dd5cd4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -206,9 +206,9 @@ class SalesInvoice(SellingController): total_amount_in_payments = 0 for payment in self.payments: total_amount_in_payments += payment.amount - - if total_amount_in_payments < self.rounded_total: - frappe.throw(_("Total payments amount can't be greater than {}".format(-self.rounded_total))) + invoice_total = self.rounded_total or self.grand_total + if total_amount_in_payments < invoice_total: + frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total))) def validate_pos_paid_amount(self): if len(self.payments) == 0 and self.is_pos: @@ -1510,4 +1510,4 @@ def create_invoice_discounting(source_name, target_doc=None): "outstanding_amount": invoice.outstanding_amount }) - return invoice_discounting \ No newline at end of file + return invoice_discounting diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 3834c96bfb..b5a02d0e49 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1762,18 +1762,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.si_docs = this.get_submitted_invoice() || []; this.email_queue_list = this.get_email_queue() || {}; this.customers_list = this.get_customers_details() || {}; - if(this.customer_doc) { - this.freeze = this.customer_doc.display - } - - freeze_screen = this.freeze_screen || false; - - if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) { - this.freeze = true; + if (this.si_docs.length || this.email_queue_list || this.customers_list) { frappe.call({ method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", - freeze: freeze_screen, + freeze: true, args: { doc_list: me.si_docs, email_queue_list: me.email_queue_list, diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 7d9acf26ed..97ce4f21e8 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -135,11 +135,11 @@ def get_chart_data(filters, columns, asset, liability, equity): datasets = [] if asset_data: - datasets.append({'name':'Assets', 'values': asset_data}) + datasets.append({'name': _('Assets'), 'values': asset_data}) if liability_data: - datasets.append({'name':'Liabilities', 'values': liability_data}) + datasets.append({'name': _('Liabilities'), 'values': liability_data}) if equity_data: - datasets.append({'name':'Equity', 'values': equity_data}) + datasets.append({'name': _('Equity'), 'values': equity_data}) chart = { "data": { diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js index ca243944e9..2343eaa846 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js @@ -27,8 +27,8 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = { fieldname:"payment_type", label: __("Payment Type"), fieldtype: "Select", - options: "Incoming\nOutgoing", - default: "Incoming" + options: __("Incoming") + "\n" + __("Outgoing"), + default: __("Incoming") }, { "fieldname":"party_type", diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py index 89e0113fc8..24b5d87b5b 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py @@ -39,8 +39,8 @@ def execute(filters=None): return columns, data def validate_filters(filters): - if (filters.get("payment_type") == "Incoming" and filters.get("party_type") == "Supplier") or \ - (filters.get("payment_type") == "Outgoing" and filters.get("party_type") == "Customer"): + if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \ + (filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"): frappe.throw(_("{0} payment entries can not be filtered by {1}")\ .format(filters.payment_type, filters.party_type)) @@ -51,7 +51,7 @@ def get_columns(filters): _("Party Type") + "::100", _("Party") + ":Dynamic Link/Party Type:140", _("Posting Date") + ":Date:100", - _("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"), + _("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"), _("Invoice Posting Date") + ":Date:130", _("Payment Due Date") + ":Date:130", _("Debit") + ":Currency:120", @@ -69,7 +69,7 @@ def get_conditions(filters): conditions = [] if not filters.party_type: - if filters.payment_type == "Outgoing": + if filters.payment_type == _("Outgoing"): filters.party_type = "Supplier" else: filters.party_type = "Customer" @@ -101,7 +101,7 @@ def get_entries(filters): def get_invoice_posting_date_map(filters): invoice_details = {} - dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice" + dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice" for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1): invoice_details[t.name] = t diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index ac11868cab..d500c8116e 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -75,11 +75,11 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss): datasets = [] if income_data: - datasets.append({'name': 'Income', 'values': income_data}) + datasets.append({'name': _('Income'), 'values': income_data}) if expense_data: - datasets.append({'name': 'Expense', 'values': expense_data}) + datasets.append({'name': _('Expense'), 'values': expense_data}) if net_profit: - datasets.append({'name': 'Net Profit/Loss', 'values': net_profit}) + datasets.append({'name': _('Net Profit/Loss'), 'values': net_profit}) chart = { "data": { diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 45d2ec2c51..ab37e91538 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -255,9 +255,15 @@ class Asset(AccountsController): precision = self.precision("gross_purchase_amount") if row.depreciation_method in ("Straight Line", "Manual"): + depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked)) + + if not depreciation_left: + frappe.msgprint(_("All the depreciations has been booked")) + depreciation_amount = flt(row.expected_value_after_useful_life) + return depreciation_amount + depreciation_amount = (flt(row.value_after_depreciation) - - flt(row.expected_value_after_useful_life)) / (cint(row.total_number_of_depreciations) - - cint(self.number_of_depreciations_booked)) + flt(row.expected_value_after_useful_life)) / depreciation_left else: depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision) @@ -275,7 +281,7 @@ class Asset(AccountsController): flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount')) - if (row.expected_value_after_useful_life and + if (row.expected_value_after_useful_life and row.expected_value_after_useful_life < asset_value_after_full_schedule): frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}") .format(row.idx, asset_value_after_full_schedule)) diff --git a/erpnext/config/crm.py b/erpnext/config/crm.py index 70784f3d5f..eba6c7a02a 100644 --- a/erpnext/config/crm.py +++ b/erpnext/config/crm.py @@ -41,6 +41,11 @@ def get_data(): "name": "Lead Source", "description": _("Track Leads by Lead Source.") }, + { + "type": "doctype", + "name": "Contract", + "description": _("Helps you keep tracks of Contracts based on Supplier, Customer and Employee"), + }, ] }, { diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 2cbe596770..52d58dce62 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -45,6 +45,7 @@ class SellingController(StockController): self.set_gross_profit() set_default_income_account_for_item(self) self.set_customer_address() + self.validate_for_duplicate_items() def set_missing_values(self, for_validate=False): @@ -381,6 +382,34 @@ class SellingController(StockController): if self.get(address_field): self.set(address_display_field, get_address_display(self.get(address_field))) + def validate_for_duplicate_items(self): + check_list, chk_dupl_itm = [], [] + if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): + return + + for d in self.get('items'): + if self.doctype == "Sales Invoice": + e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or ''] + f = [d.item_code, d.description, d.sales_order or d.delivery_note] + elif self.doctype == "Delivery Note": + e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] + f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] + elif self.doctype == "Sales Order": + e = [d.item_code, d.description, d.warehouse, d.batch_no or ''] + f = [d.item_code, d.description] + + if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: + if e in check_list: + frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code)) + else: + check_list.append(e) + else: + if f in chk_dupl_itm: + frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code)) + else: + chk_dupl_itm.append(f) + + def validate_items(self): # validate items to see if they have is_sales_item enabled from erpnext.controllers.buying_controller import validate_item_type diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js index f69b182737..cbb4b73227 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_list.js +++ b/erpnext/hr/doctype/leave_application/leave_application_list.js @@ -5,6 +5,8 @@ frappe.listview_settings['Leave Application'] = { return [__("Approved"), "green", "status,=,Approved"]; } else if (doc.status === "Rejected") { return [__("Rejected"), "red", "status,=,Rejected"]; + } else { + return [__("Open"), "red", "status,=,Open"]; } } }; diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py index e2989e29c0..e5622b7ae1 100644 --- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py +++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py @@ -23,7 +23,8 @@ def get_columns(): _("Model") + ":data:50", _("Location") + ":data:100", _("Log") + ":Link/Vehicle Log:100", _("Odometer") + ":Int:80", _("Date") + ":Date:100", _("Fuel Qty") + ":Float:80", - _("Fuel Price") + ":Float:100",_("Service Expense") + ":Float:100" + _("Fuel Price") + ":Float:100",_("Fuel Expense") + ":Float:100", + _("Service Expense") + ":Float:100" ] return columns @@ -32,7 +33,8 @@ def get_log_data(filters): data = frappe.db.sql("""select vhcl.license_plate as "License", vhcl.make as "Make", vhcl.model as "Model", vhcl.location as "Location", log.name as "Log", log.odometer as "Odometer", - log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price" + log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price", + log.fuel_qty * log.price as "Fuel Expense" from `tabVehicle` vhcl,`tabVehicle Log` log where @@ -58,7 +60,7 @@ def get_chart_data(data,period_list): total_ser_exp=0 for row in data: if row["Date"] <= period.to_date and row["Date"] >= period.from_date: - total_fuel_exp+=flt(row["Fuel Price"]) + total_fuel_exp+=flt(row["Fuel Expense"]) total_ser_exp+=flt(row["Service Expense"]) fueldata.append([period.key,total_fuel_exp]) servicedata.append([period.key,total_ser_exp]) @@ -84,4 +86,4 @@ def get_chart_data(data,period_list): } } chart["type"] = "line" - return chart \ No newline at end of file + return chart diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7b8280975d..dc6c6daa13 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -605,7 +605,6 @@ erpnext.patches.v11_1.rename_depends_on_lwp execute:frappe.delete_doc("Report", "Inactive Items") erpnext.patches.v11_1.delete_scheduling_tool erpnext.patches.v12_0.rename_tolerance_fields -erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019 execute:frappe.delete_doc_if_exists("Page", "support-analytics") erpnext.patches.v12_0.remove_patient_medical_record_page erpnext.patches.v11_1.move_customer_lead_to_dynamic_column @@ -626,4 +625,5 @@ erpnext.patches.v12_0.add_default_buying_selling_terms_in_company erpnext.patches.v12_0.update_ewaybill_field_position erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes erpnext.patches.v11_1.set_status_for_material_request_type_manufacture -erpnext.patches.v12_0.generate_leave_ledger_entries \ No newline at end of file +erpnext.patches.v12_0.remove_bank_remittance_custom_fields +erpnext.patches.v12_0.generate_leave_ledger_entries diff --git a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py b/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py deleted file mode 100644 index 3d4a9952c7..0000000000 --- a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.regional.india.setup import make_custom_fields - -def execute(): - frappe.reload_doc("accounts", "doctype", "tax_category") - frappe.reload_doc("stock", "doctype", "item_manufacturer") - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - make_custom_fields() \ No newline at end of file diff --git a/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py new file mode 100644 index 0000000000..d1446b3227 --- /dev/null +++ b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py @@ -0,0 +1,14 @@ +from __future__ import unicode_literals +import frappe +from erpnext.regional.india.setup import make_custom_fields + +def execute(): + frappe.reload_doc("accounts", "doctype", "tax_category") + frappe.reload_doc("stock", "doctype", "item_manufacturer") + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + if frappe.db.exists("Custom Field", "Company-bank_remittance_section"): + deprecated_fields = ['bank_remittance_section', 'client_code', 'remittance_column_break', 'product_code'] + for i in range(len(deprecated_fields)): + frappe.delete_doc("Custom Field", 'Company-'+deprecated_fields[i]) \ No newline at end of file diff --git a/erpnext/regional/india/bank_remittance.py b/erpnext/regional/india/bank_remittance.py deleted file mode 100644 index 85c9564722..0000000000 --- a/erpnext/regional/india/bank_remittance.py +++ /dev/null @@ -1,190 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document -from frappe.utils import cint,cstr, today -from frappe import _ -import re -import datetime -from collections import OrderedDict - -def create_bank_remittance_txt(name): - payment_order = frappe.get_cached_doc("Payment Order", name) - - no_of_records = len(payment_order.get("references")) - total_amount = sum(entry.get("amount") for entry in payment_order.get("references")) - - product_code, client_code, company_email = frappe.db.get_value("Company", - filters={'name' : payment_order.company}, - fieldname=['product_code', 'client_code', 'email']) - - header, file_name = get_header_row(payment_order, client_code) - batch = get_batch_row(payment_order, no_of_records, total_amount, product_code) - - detail = [] - for ref_doc in payment_order.get("references"): - detail += get_detail_row(ref_doc, payment_order, company_email) - - trailer = get_trailer_row(no_of_records, total_amount) - detail_records = "\n".join(detail) - - return "\n".join([header, batch, detail_records, trailer]), file_name - -@frappe.whitelist() -def generate_report(name): - data, file_name = create_bank_remittance_txt(name) - - f = frappe.get_doc({ - 'doctype': 'File', - 'file_name': file_name, - 'content': data, - "attached_to_doctype": 'Payment Order', - "attached_to_name": name, - 'is_private': True - }) - f.save() - return { - 'file_url': f.file_url, - 'file_name': file_name - } - -def generate_file_name(name, company_account, date): - ''' generate file name with format (account_code)_mmdd_(payment_order_no) ''' - bank, acc_no = frappe.db.get_value("Bank Account", {"name": company_account}, ['bank', 'bank_account_no']) - return bank[:1]+str(acc_no)[-4:]+'_'+date.strftime("%m%d")+sanitize_data(name, '')[4:]+'.txt' - -def get_header_row(doc, client_code): - ''' Returns header row and generated file name ''' - file_name = generate_file_name(doc.name, doc.company_bank_account, doc.posting_date) - header = ["H"] - header.append(validate_field_size(client_code, "Client Code", 20)) - header += [''] * 3 - header.append(validate_field_size(file_name, "File Name", 20)) - return "~".join(header), file_name - -def get_batch_row(doc, no_of_records, total_amount, product_code): - batch = ["B"] - batch.append(validate_field_size(no_of_records, "No Of Records", 5)) - batch.append(validate_amount(format(total_amount, '0.2f'), 17)) - batch.append(sanitize_data(doc.name, '_')[:20]) - batch.append(format_date(doc.posting_date)) - batch.append(validate_field_size(product_code,"Product Code", 20)) - return "~".join(batch) - -def get_detail_row(ref_doc, payment_entry, company_email): - - payment_date = format_date(payment_entry.posting_date) - payment_entry = frappe.get_cached_doc('Payment Entry', ref_doc.payment_entry) - supplier_bank_details = frappe.get_cached_doc('Bank Account', ref_doc.bank_account) - company_bank_acc_no = frappe.db.get_value("Bank Account", {'name': payment_entry.bank_account}, ['bank_account_no']) - - addr_link = frappe.db.get_value('Dynamic Link', - { - 'link_doctype': 'Supplier', - 'link_name': 'Sample Supplier', - 'parenttype':'Address', - 'parent': ('like', '%-Billing') - }, 'parent') - - supplier_billing_address = frappe.get_cached_doc('Address', addr_link) - email = ','.join(filter(None, [supplier_billing_address.email_id, company_email])) - - detail = OrderedDict( - record_identifier='D', - payment_ref_no=sanitize_data(ref_doc.payment_entry), - payment_type=cstr(payment_entry.mode_of_payment)[:10], - amount=str(validate_amount(format(ref_doc.amount, '.2f'),13)), - payment_date=payment_date, - instrument_date=payment_date, - instrument_number='', - dr_account_no_client=str(validate_field_size(company_bank_acc_no, "Company Bank Account", 20)), - dr_description='', - dr_ref_no='', - cr_ref_no='', - bank_code_indicator='M', - beneficiary_code='', - beneficiary_name=sanitize_data(validate_information(payment_entry, "party", 160), ' '), - beneficiary_bank=sanitize_data(validate_information(supplier_bank_details, "bank", 10)), - beneficiary_branch_code=cstr(validate_information(supplier_bank_details, "branch_code", 11)), - beneficiary_acc_no=validate_information(supplier_bank_details, "bank_account_no", 20), - location='', - print_location='', - beneficiary_address_1=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line1), ' '), " Beneficiary Address 1", 50), - beneficiary_address_2=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line2), ' '), " Beneficiary Address 2", 50), - beneficiary_address_3='', - beneficiary_address_4='', - beneficiary_address_5='', - beneficiary_city=validate_field_size(cstr(supplier_billing_address.city), "Beneficiary City", 20), - beneficiary_zipcode=validate_field_size(cstr(supplier_billing_address.pincode), "Pin Code", 6), - beneficiary_state=validate_field_size(cstr(supplier_billing_address.state), "Beneficiary State", 20), - beneficiary_email=cstr(email)[:255], - beneficiary_mobile=validate_field_size(cstr(supplier_billing_address.phone), "Beneficiary Mobile", 10), - payment_details_1='', - payment_details_2='', - payment_details_3='', - payment_details_4='', - delivery_mode='' - ) - detail_record = ["~".join(list(detail.values()))] - - detail_record += get_advice_rows(payment_entry) - return detail_record - -def get_advice_rows(payment_entry): - ''' Returns multiple advice rows for a single detail entry ''' - payment_entry_date = payment_entry.posting_date.strftime("%b%y%d%m").upper() - mode_of_payment = payment_entry.mode_of_payment - advice_rows = [] - for record in payment_entry.references: - advice = ['E'] - advice.append(cstr(mode_of_payment)) - advice.append(cstr(record.total_amount)) - advice.append('') - advice.append(cstr(record.outstanding_amount)) - advice.append(record.reference_name) - advice.append(format_date(record.due_date)) - advice.append(payment_entry_date) - advice_rows.append("~".join(advice)) - return advice_rows - -def get_trailer_row(no_of_records, total_amount): - ''' Returns trailer row ''' - trailer = ["T"] - trailer.append(validate_field_size(no_of_records, "No of Records", 5)) - trailer.append(validate_amount(format(total_amount, "0.2f"), 17)) - return "~".join(trailer) - -def sanitize_data(val, replace_str=''): - ''' Remove all the non-alphanumeric characters from string ''' - pattern = re.compile('[\W_]+') - return pattern.sub(replace_str, val) - -def format_date(val): - ''' Convert a datetime object to DD/MM/YYYY format ''' - return val.strftime("%d/%m/%Y") - -def validate_amount(val, max_int_size): - ''' Validate amount to be within the allowed limits ''' - int_size = len(str(val).split('.')[0]) - - if int_size > max_int_size: - frappe.throw(_("Amount for a single transaction exceeds maximum allowed amount, create a separate payment order by splitting the transactions")) - - return val - -def validate_information(obj, attr, max_size): - ''' Checks if the information is not set in the system and is within the size ''' - if hasattr(obj, attr): - return validate_field_size(getattr(obj, attr), frappe.unscrub(attr), max_size) - - else: - frappe.throw(_("{0} is mandatory for generating remittance payments, set the field and try again".format(frappe.unscrub(attr)))) - -def validate_field_size(val, label, max_size): - ''' check the size of the val ''' - if len(cstr(val)) > max_size: - frappe.throw(_("{0} field is limited to size {1}".format(label, max_size))) - return cstr(val) \ No newline at end of file diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 40b98ed19a..756c17dc3b 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -407,14 +407,6 @@ def make_custom_fields(update=True): fieldtype='Link', options='Salary Component', insert_after='basic_component'), dict(fieldname='arrear_component', label='Arrear Component', fieldtype='Link', options='Salary Component', insert_after='hra_component'), - dict(fieldname='bank_remittance_section', label='Bank Remittance Settings', - fieldtype='Section Break', collapsible=1, insert_after='arrear_component'), - dict(fieldname='client_code', label='Client Code', fieldtype='Data', - insert_after='bank_remittance_section'), - dict(fieldname='remittance_column_break', fieldtype='Column Break', - insert_after='client_code'), - dict(fieldname='product_code', label='Product Code', fieldtype='Data', - insert_after='remittance_column_break'), ], 'Employee Tax Exemption Declaration':[ dict(fieldname='hra_section', label='HRA Exemption', diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 09dc9a9932..4342af5e19 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -72,9 +72,7 @@ class SalesOrder(SellingController): frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no)) def validate_for_items(self): - check_list = [] for d in self.get('items'): - check_list.append(cstr(d.item_code)) # used for production plan d.transaction_date = self.transaction_date @@ -83,13 +81,6 @@ class SalesOrder(SellingController): where item_code = %s and warehouse = %s", (d.item_code, d.warehouse)) d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0 - # check for same entry multiple times - unique_chk_list = set(check_list) - if len(unique_chk_list) != len(check_list) and \ - not cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): - frappe.msgprint(_("Same item has been entered multiple times"), - title=_("Warning"), indicator='orange') - def product_bundle_has_stock_item(self, product_bundle): """Returns true if product bundle has stock item""" ret = len(frappe.db.sql("""select i.name from tabItem i, `tabProduct Bundle Item` pbi 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 d233b41787..d2c2d70dbe 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -4,7 +4,7 @@ frappe.provide('erpnext.pos'); frappe.pages['point-of-sale'].on_page_load = function(wrapper) { frappe.ui.make_app_page({ parent: wrapper, - title: 'Point of Sale', + title: __('Point of Sale'), single_column: true }); diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 2de9b975da..f79d127984 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -166,24 +166,7 @@ class DeliveryNote(SellingController): frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project)) def validate_for_items(self): - check_list, chk_dupl_itm = [], [] - if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): - return - for d in self.get('items'): - e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] - f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] - - if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: - if e in check_list: - frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code)) - else: - check_list.append(e) - else: - if f in chk_dupl_itm: - frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code)) - else: - chk_dupl_itm.append(f) #Customer Provided parts will have zero valuation rate if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): d.allow_zero_valuation_rate = 1 diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 1568729344..518fe74889 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -124,6 +124,7 @@ class Item(WebsiteGenerator): self.update_defaults_from_item_group() self.validate_auto_reorder_enabled_in_stock_settings() self.cant_change() + self.update_show_in_website() if not self.get("__islocal"): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") @@ -476,6 +477,10 @@ class Item(WebsiteGenerator): [self.remove(d) for d in to_remove] + def update_show_in_website(self): + if self.disabled: + self.show_in_website = False + def update_template_tables(self): template = frappe.get_doc("Item", self.variant_of) diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index faf5e9278c..8d5523ff97 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -1 +1,3 @@ -Powered by ERPNext +{% set domains = frappe.get_doc("Domain Settings").active_domains %} + +Powered by ERPNext - {{ domains[0].domain if domains else 'Open Source' }} ERP Software