diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6f73741e85..7046172c9a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -__version__ = '8.0.26' +__version__ = '8.0.27' 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 50bfbd3ea1..2eef79cd76 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -147,6 +147,7 @@ frappe.ui.form.on('Payment Entry', { var currency_field = (frm.doc.payment_type=="Receive") ? "paid_from_account_currency" : "paid_to_account_currency" frm.set_df_property("total_allocated_amount", "options", currency_field); frm.set_df_property("unallocated_amount", "options", currency_field); + frm.set_df_property("party_balance", "options", currency_field); frm.set_currency_labels(["total_amount", "outstanding_amount", "allocated_amount"], party_account_currency, "references"); diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index b913b6b632..71897d4b0c 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -285,9 +285,10 @@ def get_pricing_rules(args): def filter_pricing_rules(args, pricing_rules): # filter for qty + stock_qty = args.get('qty') * args.get('conversion_factor', 1) if pricing_rules: - pricing_rules = filter(lambda x: (flt(args.get("qty"))>=flt(x.min_qty) - and (flt(args.get("qty"))<=x.max_qty if x.max_qty else True)), pricing_rules) + pricing_rules = filter(lambda x: (flt(stock_qty)>=flt(x.min_qty) + and (flt(stock_qty)<=x.max_qty if x.max_qty else True)), pricing_rules) # add variant_of property in pricing rule for p in pricing_rules: diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 814c5c0acf..31b1d469cc 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -5,6 +5,9 @@ from __future__ import unicode_literals import unittest import frappe +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.stock.get_item_details import get_item_details +from frappe import MandatoryError class TestPricingRule(unittest.TestCase): def test_pricing_rule_for_discount(self): @@ -203,3 +206,46 @@ class TestPricingRule(unittest.TestCase): details = get_item_details(args) self.assertEquals(details.get("discount_percentage"), 17.5) + + def test_pricing_rule_for_stock_qty(self): + frappe.db.sql("delete from `tabPricing Rule`") + + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule", + "apply_on": "Item Code", + "item_code": "_Test Item", + "selling": 1, + "price_or_discount": "Discount Percentage", + "price": 0, + "min_qty": 5, + "max_qty": 7, + "discount_percentage": 17.5, + "company": "_Test Company" + } + frappe.get_doc(test_record.copy()).insert() + + if not frappe.db.get_value('UOM Conversion Detail', + {'parent': '_Test Item', 'uom': 'box'}): + item = frappe.get_doc('Item', '_Test Item') + item.append('uoms', { + 'uom': 'Box', + 'conversion_factor': 5 + }) + item.save(ignore_permissions=True) + + # With pricing rule + so = make_sales_order(item_code="_Test Item", qty=1, uom="Box", do_not_submit=True) + so.items[0].price_list_rate = 100 + so.submit() + so = frappe.get_doc('Sales Order', so.name) + self.assertEquals(so.items[0].discount_percentage, 17.5) + self.assertEquals(so.items[0].rate, 82.5) + + # Without pricing rule + so = make_sales_order(item_code="_Test Item", qty=2, uom="Box", do_not_submit=True) + so.items[0].price_list_rate = 100 + so.submit() + so = frappe.get_doc('Sales Order', so.name) + self.assertEquals(so.items[0].discount_percentage, 0) + self.assertEquals(so.items[0].rate, 100) \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 3762b4841c..63de8789c1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -49,29 +49,37 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ } if(doc.docstatus===0) { - cur_frm.add_custom_button(__('Purchase Order'), function() { + var me = this; + this.frm.add_custom_button(__('Purchase Order'), function() { erpnext.utils.map_current_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice", source_doctype: "Purchase Order", + target: me.frm, + setters: { + supplier: me.frm.doc.supplier || undefined, + }, get_query_filters: { - supplier: cur_frm.doc.supplier || undefined, docstatus: 1, status: ["!=", "Closed"], per_billed: ["<", 99.99], - company: cur_frm.doc.company + company: me.frm.doc.company } }) }, __("Get items from")); - cur_frm.add_custom_button(__('Purchase Receipt'), function() { + this.frm.add_custom_button(__('Purchase Receipt'), function() { erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice", source_doctype: "Purchase Receipt", + target: me.frm, + date_field: "posting_date", + setters: { + supplier: me.frm.doc.supplier || undefined, + }, get_query_filters: { - supplier: cur_frm.doc.supplier || undefined, docstatus: 1, status: ["!=", "Closed"], - company: cur_frm.doc.company + company: me.frm.doc.company } }) }, __("Get items from")); @@ -120,7 +128,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ hide_fields(this.frm.doc); if(cint(this.frm.doc.is_paid)) { if(!this.frm.doc.company) { - cur_frm.set_value("is_paid", 0) + this.frm.set_value("is_paid", 0) msgprint(__("Please specify Company to proceed")); } } diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index e8163f0a1c..f2357226cd 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -112,33 +112,43 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte }, sales_order_btn: function() { - this.$sales_order_btn = cur_frm.add_custom_button(__('Sales Order'), + var me = this; + this.$sales_order_btn = this.frm.add_custom_button(__('Sales Order'), function() { erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_sales_invoice", source_doctype: "Sales Order", + target: me.frm, + setters: { + customer: me.frm.doc.customer || undefined, + }, get_query_filters: { docstatus: 1, status: ["!=", "Closed"], per_billed: ["<", 99.99], - customer: cur_frm.doc.customer || undefined, - company: cur_frm.doc.company + company: me.frm.doc.company } }) }, __("Get items from")); }, delivery_note_btn: function() { - this.$delivery_note_btn = cur_frm.add_custom_button(__('Delivery Note'), + var me = this; + this.$delivery_note_btn = this.frm.add_custom_button(__('Delivery Note'), function() { erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice", source_doctype: "Delivery Note", + target: me.frm, + date_field: "posting_date", + setters: { + company: me.frm.doc.company + }, get_query: function() { var filters = { - company: cur_frm.doc.company + docstatus: 1, }; - if(cur_frm.doc.customer) filters["customer"] = cur_frm.doc.customer; + if(me.frm.doc.customer) filters["customer"] = me.frm.doc.customer; return { query: "erpnext.controllers.queries.get_delivery_notes_to_be_billed", filters: filters diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 295f649d60..285722b1ad 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -52,7 +52,7 @@ class SalesInvoice(SellingController): def validate(self): super(SalesInvoice, self).validate() - self.validate_posting_time() + self.validate_auto_set_posting_time() self.so_dn_required() self.validate_proj_cust() self.validate_with_previous_doc() @@ -378,6 +378,12 @@ class SalesInvoice(SellingController): def add_remarks(self): if not self.remarks: self.remarks = 'No Remarks' + def validate_auto_set_posting_time(self): + # Don't auto set the posting date and time if invoice is amended + if self.is_new() and self.amended_from: + self.set_posting_time = 1 + + self.validate_posting_time() def so_dn_required(self): """check in manage account if sales order / delivery note required or not.""" diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index c632e459e3..da4a3b7f25 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -78,8 +78,16 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ make_menu_list: function () { var me = this; - this.page.clear_menu(); + + // for mobile + this.page.add_menu_item(__("Pay"), function () { + me.validate(); + me.update_paid_amount_status(true); + me.create_invoice(); + me.make_payment(); + }).addClass('visible-xs'); + this.page.add_menu_item(__("New Sales Invoice"), function () { me.save_previous_entry(); me.create_new(); @@ -788,7 +796,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ add_customer: function() { this.frm.doc.customer = ""; - this.update_customer(true) + this.update_customer(true); + this.numeric_keypad.show(); }, update_customer: function (new_customer) { diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 85a6329f51..cfd333670a 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -153,30 +153,37 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( }, add_from_mappers: function() { - cur_frm.add_custom_button(__('Material Request'), + var me = this; + this.frm.add_custom_button(__('Material Request'), function() { erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order", source_doctype: "Material Request", + target: me.frm, + setters: { + company: me.frm.doc.company + }, get_query_filters: { material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], per_ordered: ["<", 99.99], - company: cur_frm.doc.company } }) }, __("Add items from")); - cur_frm.add_custom_button(__('Supplier Quotation'), + this.frm.add_custom_button(__('Supplier Quotation'), function() { erpnext.utils.map_current_doc({ method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order", source_doctype: "Supplier Quotation", + target: me.frm, + setters: { + company: me.frm.doc.company + }, get_query_filters: { docstatus: 1, status: ["!=", "Stopped"], - company: cur_frm.doc.company } }) }, __("Add items from")); diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 92600b774c..593f667994 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -48,7 +48,7 @@ frappe.ui.form.on("Request for Quotation",{ }); }); } - + }, make_suppplier_quotation: function(frm) { @@ -124,24 +124,28 @@ frappe.ui.form.on("Request for Quotation Supplier",{ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.extend({ refresh: function() { + var me = this; this._super(); if (this.frm.doc.docstatus===0) { - cur_frm.add_custom_button(__('Material Request'), + this.frm.add_custom_button(__('Material Request'), function() { erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.material_request.material_request.make_request_for_quotation", source_doctype: "Material Request", + target: me.frm, + setters: { + company: me.frm.doc.company + }, get_query_filters: { material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], - per_ordered: ["<", 99.99], - company: cur_frm.doc.company + per_ordered: ["<", 99.99] } }) }, __("Get items from")); // Get items from open Material Requests based on supplier - cur_frm.add_custom_button(__('Possible Supplier'), function() { + this.frm.add_custom_button(__('Possible Supplier'), function() { // Create a dialog window for the user to pick their supplier var d = new frappe.ui.Dialog({ title: __('Select Possible Supplier'), @@ -150,32 +154,35 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e {fieldname: 'ok_button', fieldtype:'Button', label:'Get Items from Material Requests'}, ] }); - + // On the user clicking the ok button d.fields_dict.ok_button.input.onclick = function() { var btn = d.fields_dict.ok_button.input; var v = d.get_values(); if(v) { $(btn).set_working(); - + erpnext.utils.map_current_doc({ method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_item_from_material_requests_based_on_supplier", source_name: v.supplier, + target: me.frm, + setters: { + company: me.frm.doc.company + }, get_query_filters: { material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], - per_ordered: ["<", 99.99], - company: cur_frm.doc.company + per_ordered: ["<", 99.99] } }); $(btn).done_working(); d.hide(); } - } + } d.show(); }, __("Get items from")); - + } }, diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index 1e2379e1a0..6bcbdbacac 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -14,6 +14,7 @@ frappe.ui.form.on('Suppier Quotation', { erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({ refresh: function() { + var me = this; this._super(); if (this.frm.doc.docstatus === 1) { cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order, @@ -24,18 +25,21 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext } else if (this.frm.doc.docstatus===0) { - - cur_frm.add_custom_button(__('Material Request'), + + this.frm.add_custom_button(__('Material Request'), function() { erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.material_request.material_request.make_supplier_quotation", source_doctype: "Material Request", + target: me.frm, + setters: { + company: me.frm.doc.company + }, get_query_filters: { material_request_type: "Purchase", docstatus: 1, status: ["!=", "Stopped"], - per_ordered: ["<", 99.99], - company: cur_frm.doc.company + per_ordered: ["<", 99.99] } }) }, __("Get items from")); diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index d58ba4b841..403430606f 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -46,8 +46,8 @@ status_map = { ["Draft", None], ["Submitted", "eval:self.docstatus==1"], ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1 and self.is_return==0"], + ["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"], + ["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], ["Cancelled", "eval:self.docstatus==2"], @@ -56,8 +56,8 @@ status_map = { ["Draft", None], ["Submitted", "eval:self.docstatus==1"], ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1 and self.is_return==0"], + ["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"], + ["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], ["Cancelled", "eval:self.docstatus==2"], @@ -119,7 +119,8 @@ class StatusUpdater(Document): self.status = s[0] break elif s[1].startswith("eval:"): - if frappe.safe_eval(s[1][5:], None, { "self": self.as_dict(), "getdate": getdate, "nowdate": nowdate }): + if frappe.safe_eval(s[1][5:], None, { "self": self.as_dict(), "getdate": getdate, + "nowdate": nowdate, "get_value": frappe.db.get_value }): self.status = s[0] break elif getattr(self, s[1])(): diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 7889ead057..c1c1fd7cda 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -465,6 +465,8 @@ class calculate_taxes_and_totals(object): payment.base_amount = flt(payment.amount * self.doc.conversion_rate) paid_amount += payment.amount base_paid_amount += payment.base_amount + elif not self.doc.is_return: + self.doc.set('payments', []) self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount")) self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount")) diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py new file mode 100644 index 0000000000..0e2d6d0b4e --- /dev/null +++ b/erpnext/controllers/tests/test_mapper.py @@ -0,0 +1,73 @@ +from __future__ import unicode_literals +import unittest +import frappe + +import random, json +import frappe.utils +from frappe.utils import nowdate +from frappe.model import mapper +from frappe.test_runner import make_test_records + +class TestMapper(unittest.TestCase): + def test_map_docs(self): + '''Test mapping of multiple source docs on a single target doc''' + + make_test_records("Item") + items = frappe.get_all("Item", fields = ["name", "item_code"], filters = {'is_sales_item': 1, 'has_variants': 0}) + customers = frappe.get_all("Customer") + if items and customers: + # Make source docs (quotations) and a target doc (sales order) + customer = random.choice(customers).name + qtn1, item_list_1 = self.make_quotation(items, customer) + qtn2, item_list_2 = self.make_quotation(items, customer) + so, item_list_3 = self.make_sales_order() + + # Map source docs to target with corresponding mapper method + method = "erpnext.selling.doctype.quotation.quotation.make_sales_order" + updated_so = mapper.map_docs(method, json.dumps([qtn1.name, qtn2.name]), so) + + # Assert that all inserted items are present in updated sales order + src_items = item_list_1 + item_list_2 + item_list_3 + self.assertEqual(set([d.item_code for d in src_items]), + set([d.item_code for d in updated_so.items])) + + def get_random_items(self, items, limit): + '''Get a number of random items from a list of given items''' + random_items = [] + for i in range(0, limit): + random_items.append(random.choice(items)) + return random_items + + def make_quotation(self, items, customer): + item_list = self.get_random_items(items, 3) + qtn = frappe.get_doc({ + "doctype": "Quotation", + "quotation_to": "Customer", + "customer": customer, + "order_type": "Sales" + }) + for item in item_list: + qtn.append("items", {"qty": "2", "item_code": item.item_code}) + + qtn.submit() + return qtn, item_list + + def make_sales_order(self): + item = frappe.get_doc({ + "base_amount": 1000.0, + "base_rate": 100.0, + "description": "CPU", + "doctype": "Sales Order Item", + "item_code": "_Test Item Home Desktop 100", + "item_name": "CPU", + "parentfield": "items", + "qty": 10.0, + "rate": 100.0, + "warehouse": "_Test Warehouse - _TC", + "stock_uom": "_Test UOM", + "conversion_factor": 1.0, + "uom": "_Test UOM" + }) + so = frappe.get_doc(frappe.get_test_records('Sales Order')[0]) + so.insert(ignore_permissions=True) + return so, [item] diff --git a/erpnext/docs/assets/img/setup/integrations/payment_gateway_account_stripe.png b/erpnext/docs/assets/img/setup/integrations/payment_gateway_account_stripe.png new file mode 100644 index 0000000000..8ecf8dfa1b Binary files /dev/null and b/erpnext/docs/assets/img/setup/integrations/payment_gateway_account_stripe.png differ diff --git a/erpnext/docs/assets/img/setup/integrations/stripe_coa.png b/erpnext/docs/assets/img/setup/integrations/stripe_coa.png new file mode 100644 index 0000000000..27e79c5407 Binary files /dev/null and b/erpnext/docs/assets/img/setup/integrations/stripe_coa.png differ diff --git a/erpnext/docs/assets/img/setup/integrations/stripe_setting.png b/erpnext/docs/assets/img/setup/integrations/stripe_setting.png new file mode 100644 index 0000000000..b6aa124af0 Binary files /dev/null and b/erpnext/docs/assets/img/setup/integrations/stripe_setting.png differ 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 0fafa537d1..f0203627bf 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 @@ -8,7 +8,7 @@ You can manage multiple incoming and outgoing Email Accounts in ERPNext. There h ERPNext will create templates for a bunch of email accounts by default. Not all of them are enabled. To enable them, you must set your account details. -There are 2 types of email accounts, outgoing and incoming. Outgoing email accounts use an SMTP service to send emails and emails are retrived from your inbox using a POP service. Most email providers such as GMail, Outlook or Yahoo provide these services. +There are 2 types of email accounts, outgoing and incoming. Outgoing email accounts use an SMTP service to send emails and emails are retrived from your inbox using a IMAP or POP service. Most email providers such as GMail, Outlook or Yahoo provide these services. Defining Criteria @@ -28,7 +28,7 @@ To setup an incoming Email Account, check on **Enable Incoming** and set your PO ### How ERPNext handles replies -In ERPNext when you send an email to a contact like a customer, the sender will be the user who sent the email. In the **Reply-To** property, the Email Address will be of the default incoming account (like `replies@yourcompany.com`). ERPNext will automatically extract these emails from the incoming account and tag it to the relvant communication +In ERPNext when you send an email to a contact like a customer, the sender will be the user who sent the email. In the **Reply-To** property, the Email Address will be of the default incoming account (like `replies@yourcompany.com`). ERPNext will automatically extract these emails from the incoming account and tag it to the relevant communication ### Notification for unreplied messages diff --git a/erpnext/docs/user/manual/en/setting-up/integrations/stripe-integration.md b/erpnext/docs/user/manual/en/setting-up/integrations/stripe-integration.md new file mode 100644 index 0000000000..05f70f0d4a --- /dev/null +++ b/erpnext/docs/user/manual/en/setting-up/integrations/stripe-integration.md @@ -0,0 +1,30 @@ +#Setting up Stripe + +To setup Stripe, +`Explore > Integrations > Stripe Settings` + +#### Setup Stripe + +To enable Stripe payment service, you need to configure parameters like Publishable Key, Secret Key +Razorpay Settings + +On enabling service, the system will create Payment Gateway record and Account head in chart of account with account type as Bank. + +Stripe COA + +Also it will create Payment Gateway Account entry. Payment Gateway Account is configuration hub from this you can set account head from existing COA, default Payment Request email body template. + +Payment Gateway Account + +After configuring Payment Gateway Account your system is able to accept online payments. + +####Supporting transaction currencies + "AED", "ALL", "ANG", "ARS", "AUD", "AWG", "BBD", "BDT", "BIF", "BMD", "BND", + "BOB", "BRL", "BSD", "BWP", "BZD", "CAD", "CHF", "CLP", "CNY", "COP", "CRC", "CVE", "CZK", "DJF", + "DKK", "DOP", "DZD", "EGP", "ETB", "EUR", "FJD", "FKP", "GBP", "GIP", "GMD", "GNF", "GTQ", "GYD", + "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "INR", "ISK", "JMD", "JPY", "KES", "KHR", "KMF", + "KRW", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "MAD", "MDL", "MNT", "MOP", "MRO", "MUR", "MVR", + "MWK", "MXN", "MYR", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "PAB", "PEN", "PGK", "PHP", "PKR", + "PLN", "PYG", "QAR", "RUB", "SAR", "SBD", "SCR", "SEK", "SGD", "SHP", "SLL", "SOS", "STD", "SVC", + "SZL", "THB", "TOP", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VND", "VUV", "WST", + "XAF", "XOF", "XPF", "YER", "ZAR" diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js index 56c30fffc4..818a595c93 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js @@ -16,7 +16,15 @@ frappe.ui.form.on("Vehicle Log", { } }) } + + if(frm.doc.docstatus == 1) { + frm.add_custom_button(__('Expense Claim'), function() { + frm.events.expense_claim(frm) + }, __("Make")); + frm.page.set_inner_btn_group_as_primary(__("Make")); + } }, + expense_claim: function(frm){ frappe.call({ method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim", diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.json b/erpnext/hr/doctype/vehicle_log/vehicle_log.json index 3610da4baf..fde34d7c55 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.json +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "autoname": "naming_series:", @@ -12,6 +13,7 @@ "editable_grid": 1, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -41,6 +43,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -71,6 +74,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -101,6 +105,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -131,6 +136,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -159,6 +165,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -187,6 +194,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -216,6 +224,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -245,6 +254,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -274,6 +284,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -303,6 +314,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -332,6 +344,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -361,6 +374,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -390,6 +404,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -419,6 +434,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -447,6 +463,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -477,6 +494,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -506,6 +524,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -519,7 +538,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Service_Details", + "label": "Service Details", "length": 0, "no_copy": 0, "permlevel": 0, @@ -535,6 +554,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -565,64 +585,7 @@ "unique": 0 }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_20", - "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, - "unique": 0 - }, - { - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus==1", - "fieldname": "expense_claim", - "fieldtype": "Button", - "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": "Make Expense Claim", - "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, @@ -652,17 +615,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-02-17 16:53:17.975663", + "modified": "2017-05-15 13:17:59.575317", "modified_by": "Administrator", "module": "HR", "name": "Vehicle Log", diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js index 9639e7faa2..da7d133d21 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js @@ -33,10 +33,13 @@ erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({ erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_schedule", source_doctype: "Sales Order", + target: me.frm, + setters: { + customer: me.frm.doc.customer || undefined, + order_type: me.frm.doc.order_type, + }, get_query_filters: { docstatus: 1, - order_type: me.frm.doc.order_type, - customer: me.frm.doc.customer || undefined, company: me.frm.doc.company } }); diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js index 62cdf865e0..e1f501b38c 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js @@ -27,41 +27,53 @@ erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({ refresh: function() { frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'} + var me = this; + if (this.frm.doc.docstatus===0) { - cur_frm.add_custom_button(__('Maintenance Schedule'), + this.frm.add_custom_button(__('Maintenance Schedule'), function() { erpnext.utils.map_current_doc({ method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.make_maintenance_visit", source_doctype: "Maintenance Schedule", + target: me.frm, + setters: { + customer: me.frm.doc.customer || undefined, + }, get_query_filters: { docstatus: 1, - customer: cur_frm.doc.customer || undefined, - company: cur_frm.doc.company + company: me.frm.doc.company } }) }, __("Get items from")); - cur_frm.add_custom_button(__('Warranty Claim'), + this.frm.add_custom_button(__('Warranty Claim'), function() { erpnext.utils.map_current_doc({ method: "erpnext.support.doctype.warranty_claim.warranty_claim.make_maintenance_visit", source_doctype: "Warranty Claim", + target: me.frm, + date_field: "complaint_date", + setters: { + customer: me.frm.doc.customer || undefined, + }, get_query_filters: { status: ["in", "Open, Work in Progress"], - customer: cur_frm.doc.customer || undefined, - company: cur_frm.doc.company + company: me.frm.doc.company } }) }, __("Get items from")); - cur_frm.add_custom_button(__('Sales Order'), + this.frm.add_custom_button(__('Sales Order'), function() { erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_visit", source_doctype: "Sales Order", + target: me.frm, + setters: { + customer: me.frm.doc.customer || undefined, + }, get_query_filters: { docstatus: 1, - order_type: cur_frm.doc.order_type, - customer: cur_frm.doc.customer || undefined, - company: cur_frm.doc.company + company: me.frm.doc.company, + order_type: me.frm.doc.order_type, } }) }, __("Get items from")); diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 5181cf48a5..576e46df50 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -33,9 +33,7 @@ frappe.ui.form.on("BOM", { }); } - if(frm.doc.docstatus==2) { - // show duplicate button when BOM is cancelled, - // its not very intuitive + if(frm.doc.docstatus!=0) { frm.add_custom_button(__("Duplicate"), function() { frm.copy_doc(); }); diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 8907cf3e16..ac91f6038b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -393,4 +393,6 @@ erpnext.patches.v7_2.stock_uom_in_selling erpnext.patches.v8_0.revert_manufacturers_table_from_item erpnext.patches.v8_0.disable_instructor_role erpnext.patches.v8_0.merge_student_batch_and_student_group -erpnext.patches.v8_0.rename_total_margin_to_rate_with_margin # 11-05-2017 \ No newline at end of file +erpnext.patches.v8_0.rename_total_margin_to_rate_with_margin # 11-05-2017 +erpnext.patches.v8_0.fix_status_for_invoices_with_negative_outstanding +erpnext.patches.v8_0.make_payments_table_blank_for_non_pos_invoice diff --git a/erpnext/patches/v7_2/mark_students_active.py b/erpnext/patches/v7_2/mark_students_active.py index 12057edb63..3513cdeb34 100644 --- a/erpnext/patches/v7_2/mark_students_active.py +++ b/erpnext/patches/v7_2/mark_students_active.py @@ -1,7 +1,5 @@ import frappe def execute(): - frappe.reload_doc('schools', 'doctype', 'student_batch_student') frappe.reload_doc('schools', 'doctype', 'student_group_student') - frappe.db.sql("update `tabStudent Batch Student` set active=1") frappe.db.sql("update `tabStudent Group Student` set active=1") diff --git a/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py b/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py new file mode 100644 index 0000000000..2e7f360c97 --- /dev/null +++ b/erpnext/patches/v8_0/fix_status_for_invoices_with_negative_outstanding.py @@ -0,0 +1,23 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + for dt, status in [["Sales Invoice", "Credit Note Issued"], ["Purchase Invoice", "Debit Note Issued"]]: + invoices = frappe.db.sql(""" + select name + from `tab{0}` + where + status = %s + and outstanding_amount < 0 + and docstatus=1 + and is_return=0 + """.format(dt), status) + + for inv in invoices: + return_inv = frappe.db.sql("""select name from `tab{0}` + where is_return=1 and return_against=%s and docstatus=1""".format(dt), inv[0]) + if not return_inv: + frappe.db.sql("update `tab{0}` set status='Paid' where name = %s".format(dt), inv[0]) \ No newline at end of file diff --git a/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py b/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py new file mode 100644 index 0000000000..9750fb7222 --- /dev/null +++ b/erpnext/patches/v8_0/make_payments_table_blank_for_non_pos_invoice.py @@ -0,0 +1,15 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doctype('Sales Invoice') + + frappe.db.sql(""" + delete from + `tabSales Invoice Payment` + where + parent in (select name from `tabSales Invoice` where is_pos = 0) + """) \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 40493e1ed6..0345f05861 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -216,9 +216,19 @@ class Project(Document): # duplicated project dependency_map = {} for task in self.tasks: - name, depends_on_tasks = frappe.db.get_value( - 'Task', { "subject": task.title, "project": self.copied_from }, ['name', 'depends_on_tasks'] + _task = frappe.db.get_value( + 'Task', + {"subject": task.title, "project": self.copied_from}, + ['name', 'depends_on_tasks'], + as_dict=True ) + + if _task is None: + continue + + name = _task.name + depends_on_tasks = _task.depends_on_tasks + depends_on_tasks = [x for x in depends_on_tasks.split(',') if x] dependency_map[task.title] = [ x['subject'] for x in frappe.get_list( 'Task Depends On', {"parent": name}, ['subject'])] diff --git a/erpnext/public/css/erpnext.css b/erpnext/public/css/erpnext.css index fd240064ca..86dec05eb9 100644 --- a/erpnext/public/css/erpnext.css +++ b/erpnext/public/css/erpnext.css @@ -270,6 +270,11 @@ body[data-route="pos"] .item-cart-items { border: 1px solid #d1d8dd; border-top: none; } +@media (max-width: 767px) { + body[data-route="pos"] .item-cart-items { + height: 30vh; + } +} body[data-route="pos"] .no-items-message { min-height: 200px; display: flex; diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index c925f4577b..849275f02c 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -594,6 +594,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ paid_amount += data.amount; base_paid_amount += data.base_amount; }) + } else if(!this.frm.doc.is_return){ + this.frm.doc.payments = []; } this.frm.doc.paid_amount = flt(paid_amount, precision("paid_amount")); diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 8865b50198..3a2254e24c 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -114,63 +114,68 @@ erpnext.utils.map_current_doc = function(opts) { } } var _map = function() { - // remove first item row if empty if($.isArray(cur_frm.doc.items) && cur_frm.doc.items.length > 0) { + // remove first item row if empty if(!cur_frm.doc.items[0].item_code) { cur_frm.doc.items = cur_frm.doc.items.splice(1); } // find the doctype of the items table var items_doctype = frappe.meta.get_docfield(cur_frm.doctype, 'items').options; - + // find the link fieldname from items table for the given // source_doctype var link_fieldname = null; - frappe.get_meta(items_doctype).fields.forEach(function(d) { + frappe.get_meta(items_doctype).fields.forEach(function(d) { if(d.options===opts.source_doctype) link_fieldname = d.fieldname; }); // search in existing items if the source_name is already set and full qty fetched var already_set = false; var item_qty_map = {}; - - $.each(cur_frm.doc.items, function(i, d) { - if(d[link_fieldname]==opts.source_name) { - already_set = true; - if (item_qty_map[d.item_code]) - item_qty_map[d.item_code] += flt(d.qty); - else - item_qty_map[d.item_code] = flt(d.qty); - } - }); - - if(already_set) { - frappe.model.with_doc(opts.source_doctype, opts.source_name, function(r) { - var source_doc = frappe.model.get_doc(opts.source_doctype, opts.source_name); - $.each(source_doc.items || [], function(i, row) { - if(row.qty > flt(item_qty_map[row.item_code])) { - already_set = false; - return false; - } - }) - }) - if(already_set) { - frappe.msgprint(__("You have already selected items from {0} {1}", - [opts.source_doctype, opts.source_name])); - return; - } + $.each(cur_frm.doc.items, function(i, d) { + opts.source_name.forEach(function(src) { + if(d[link_fieldname]==src) { + already_set = true; + if (item_qty_map[d.item_code]) + item_qty_map[d.item_code] += flt(d.qty); + else + item_qty_map[d.item_code] = flt(d.qty); + } + }); + }); + + if(already_set) { + opts.source_name.forEach(function(src) { + frappe.model.with_doc(opts.source_doctype, src, function(r) { + var source_doc = frappe.model.get_doc(opts.source_doctype, src); + $.each(source_doc.items || [], function(i, row) { + if(row.qty > flt(item_qty_map[row.item_code])) { + already_set = false; + return false; + } + }) + }) + + if(already_set) { + frappe.msgprint(__("You have already selected items from {0} {1}", + [opts.source_doctype, src])); + return; + } + + }) } } - return frappe.call({ // Sometimes we hit the limit for URL length of a GET request // as we send the full target_doc. Hence this is a POST request. type: "POST", - method: opts.method, + method: 'frappe.model.mapper.map_docs', args: { - "source_name": opts.source_name, - "target_doc": cur_frm.doc + "method": opts.method, + "source_names": opts.source_name, + "target_doc": cur_frm.doc, }, callback: function(r) { if(!r.exc) { @@ -181,29 +186,26 @@ erpnext.utils.map_current_doc = function(opts) { }); } if(opts.source_doctype) { - var d = new frappe.ui.Dialog({ - title: __("Get From ") + __(opts.source_doctype), - fields: [ - { - fieldtype: "Link", - label: __(opts.source_doctype), - fieldname: opts.source_doctype, - options: opts.source_doctype, - get_query: opts.get_query, - reqd:1 - }, - ] + var d = new frappe.ui.form.MultiSelectDialog({ + doctype: opts.source_doctype, + target: opts.target, + date_field: opts.date_field || undefined, + setters: opts.setters, + get_query: opts.get_query, + action: function(selections, args) { + let values = selections; + if(values.length === 0){ + frappe.msgprint(__("Please select Quotations")) + return; + } + opts.source_name = values; + opts.setters = args; + d.dialog.hide(); + _map(); + }, }); - d.set_primary_action(__('Get Items'), function() { - var values = d.get_values(); - if(!values) - return; - opts.source_name = values[opts.source_doctype]; - d.hide(); - _map(); - }) - d.show(); } else if(opts.source_name) { + opts.source_name = [opts.source_name]; _map(); } } diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less index 0431848f86..f6482eb516 100644 --- a/erpnext/public/less/erpnext.less +++ b/erpnext/public/less/erpnext.less @@ -321,6 +321,10 @@ body[data-route="pos"] { overflow: auto; border: 1px solid @border-color; border-top: none; + + @media (max-width: @screen-xs) { + height: 30vh; + } } .no-items-message { diff --git a/erpnext/schools/doctype/program/program.json b/erpnext/schools/doctype/program/program.json index 9d6ba1ccbb..672994b821 100644 --- a/erpnext/schools/doctype/program/program.json +++ b/erpnext/schools/doctype/program/program.json @@ -14,6 +14,7 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -43,6 +44,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -73,6 +75,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -101,6 +104,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -130,6 +134,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -159,6 +164,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -188,6 +194,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -218,6 +225,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -247,6 +255,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -288,7 +297,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-04-12 20:40:53.542488", + "modified": "2017-05-12 15:39:15.542274", "modified_by": "Administrator", "module": "Schools", "name": "Program", diff --git a/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.js b/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.js index 9c796bb474..dd909b2db0 100644 --- a/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.js +++ b/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.js @@ -6,20 +6,27 @@ frappe.ui.form.on("Student Group Creation Tool", "refresh", function(frm) { doc:frm.doc }) }); - + frappe.realtime.on("student_group_creation_progress", function(data) { + if(data.progress) { + frappe.hide_msgprint(true); + frappe.show_progress(__("Creating student groups"), data.progress[0],data.progress[1]); + } + }); }); frappe.ui.form.on("Student Group Creation Tool", "get_courses", function(frm) { frm.set_value("courses",[]); - frappe.call({ - method: "get_courses", - doc:frm.doc, - callback: function(r) { - if(r.message) { - frm.set_value("courses", r.message); + if (frm.doc.academic_year && frm.doc.program) { + frappe.call({ + method: "get_courses", + doc:frm.doc, + callback: function(r) { + if(r.message) { + frm.set_value("courses", r.message); + } } - } - }) + }) + } }); frappe.ui.form.on("Student Group Creation Tool", "onload", function(frm){ diff --git a/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.json b/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.json index dd011c3c38..2d543ac28a 100644 --- a/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.json +++ b/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.json @@ -1,5 +1,6 @@ { "allow_copy": 1, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -9,18 +10,23 @@ "doctype": "DocType", "document_type": "", "editable_grid": 0, + "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "academic_year", "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": "Academic Year", "length": 0, "no_copy": 0, @@ -30,6 +36,7 @@ "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, @@ -37,16 +44,21 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, + "description": "Leave blank if you make students groups per year", "fieldname": "academic_term", "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": "Academic Term", "length": 0, "no_copy": 0, @@ -56,23 +68,28 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "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": "get_courses", "fieldtype": "Button", "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": "Get Courses", "length": 0, "no_copy": 0, @@ -81,6 +98,7 @@ "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, @@ -88,16 +106,20 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "column_break_4", "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, @@ -105,6 +127,7 @@ "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, @@ -112,17 +135,21 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "description": "Leave blank if you wish to fetch all courses for selected academic term", + "columns": 0, + "description": "", "fieldname": "program", "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": "Program", "length": 0, "no_copy": 0, @@ -132,6 +159,39 @@ "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, + "description": "Leave unchecked if you don't want to consider batch while making course based groups. ", + "fieldname": "separate_groups", + "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": "Separate course based Group for every Batch", + "length": 0, + "no_copy": 0, + "options": "", + "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, @@ -139,16 +199,20 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "section_break_4", "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, @@ -156,6 +220,7 @@ "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, @@ -163,16 +228,20 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "courses", "fieldtype": "Table", "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": "Courses", "length": 0, "no_copy": 0, @@ -182,6 +251,7 @@ "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, @@ -189,17 +259,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 1, "hide_toolbar": 1, "idx": 0, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2016-07-25 06:40:46.107131", + "modified": "2017-05-15 12:43:32.317942", "modified_by": "Administrator", "module": "Schools", "name": "Student Group Creation Tool", @@ -230,7 +300,9 @@ "quick_entry": 0, "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/schools/doctype/student_group_creation_tool/student_group_creation_tool.py b/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.py index 65cf755c61..a23650882e 100644 --- a/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.py +++ b/erpnext/schools/doctype/student_group_creation_tool/student_group_creation_tool.py @@ -9,40 +9,63 @@ from frappe.model.document import Document class StudentGroupCreationTool(Document): def get_courses(self): - if self.program: - courses = frappe.db.sql("""select course, course_code, parent as program, "student_group_name" - from `tabProgram Course` where academic_term= %s and parent= %s""", - (self.academic_term, self.program), as_dict=1) + group_list = [] + + batches = frappe.db.sql('''select name as batch from `tabStudent Batch Name`''', as_dict=1) + for batch in batches: + group_list.append({"group_based_on":"Batch", "batch":batch.batch}) + + courses = frappe.db.sql('''select course, course_name from `tabProgram Course` where parent=%s''', + (self.program), as_dict=1) + if self.separate_groups: + from itertools import product + course_list = product(courses,batches) + for course in course_list: + temp_dict = {} + temp_dict.update({"group_based_on":"Course"}) + temp_dict.update(course[0]) + temp_dict.update(course[1]) + group_list.append(temp_dict) else: - courses = frappe.db.sql("""select course, course_code, parent as program, "student_group_name" - from `tabProgram Course` where academic_term= %s""", - self.academic_term, as_dict=1) + for course in courses: + course.update({"group_based_on":"Course"}) + group_list.append(course) - for d in courses: - if d.course_code: - d.student_group_name = d.course_code + "-" + self.academic_year - else: - d.student_group_name = None + for group in group_list: + if group.get("group_based_on") == "Batch": + student_group_name = self.program + "/" + group.get("batch") + "/" + (self.academic_term if self.academic_term else self.academic_year) + group.update({"student_group_name": student_group_name}) + elif group.get("group_based_on") == "Course": + student_group_name = group.get("course") + "/" + self.program + ("/" + group.get("batch") if group.get("batch") else "") + "/" + (self.academic_term if self.academic_term else self.academic_year) + group.update({"student_group_name": student_group_name}) - return courses + return group_list def create_student_groups(self): if not self.courses: frappe.throw(_("""No Student Groups created.""")) + l = len(self.courses) for d in self.courses: - if not d.course: - frappe.throw(_("""Course is mandatory in row {0}""".format(d.idx))) - if not d.student_group_name: frappe.throw(_("""Student Group Name is mandatory in row {0}""".format(d.idx))) + if d.group_based_on == "Course" and not d.course: + frappe.throw(_("""Course is mandatory in row {0}""".format(d.idx))) + + if d.group_based_on == "Batch" and not d.batch: + frappe.throw(_("""Batch is mandatory in row {0}""".format(d.idx))) + + frappe.publish_realtime('student_group_creation_progress', {"progress": [d.idx, l]}, user=frappe.session.user) + student_group = frappe.new_doc("Student Group") - student_group.group_name = d.student_group_name + student_group.student_group_name = d.student_group_name + student_group.group_based_on = d.group_based_on + student_group.program = self.program student_group.course = d.course - student_group.max_strength = d.max_strength + student_group.batch = d.batch student_group.academic_term = self.academic_term student_group.academic_year = self.academic_year student_group.save() - frappe.msgprint(_("Student Groups created.")) + frappe.msgprint(_("{0} Student Groups created.".format(l))) diff --git a/erpnext/schools/doctype/student_group_creation_tool_course/student_group_creation_tool_course.json b/erpnext/schools/doctype/student_group_creation_tool_course/student_group_creation_tool_course.json index 88466b60ab..d945d4b7e5 100644 --- a/erpnext/schools/doctype/student_group_creation_tool_course/student_group_creation_tool_course.json +++ b/erpnext/schools/doctype/student_group_creation_tool_course/student_group_creation_tool_course.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -9,18 +10,54 @@ "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": "group_based_on", + "fieldtype": "Select", + "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": "Group Based On", + "length": 0, + "no_copy": 0, + "options": "\nBatch\nCourse", + "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": "course", "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": "Course", "length": 0, "no_copy": 0, @@ -30,32 +67,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "course_code", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Course Code", - "length": 0, - "no_copy": 0, - "options": "course.course_code", - "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, @@ -63,16 +75,51 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, + "fieldname": "batch", + "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": "Batch", + "length": 0, + "no_copy": 0, + "options": "Student Batch Name", + "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_3", "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, @@ -80,6 +127,7 @@ "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, @@ -87,16 +135,20 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "student_group_name", "fieldtype": "Data", "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": "Student Group Name", "length": 0, "no_copy": 0, @@ -105,6 +157,7 @@ "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, @@ -112,16 +165,51 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, + "fieldname": "course_code", + "fieldtype": "Read Only", + "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": "Course Code", + "length": 0, + "no_copy": 0, + "options": "course.course_name", + "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, + "bold": 0, + "collapsible": 0, + "columns": 0, "fieldname": "max_strength", "fieldtype": "Int", "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": "Max Strength", "length": 0, "no_copy": 0, @@ -130,6 +218,7 @@ "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, @@ -137,17 +226,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-07-25 06:40:49.000588", + "modified": "2017-05-15 14:18:23.435415", "modified_by": "Administrator", "module": "Schools", "name": "Student Group Creation Tool Course", @@ -157,7 +246,9 @@ "quick_entry": 0, "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/schools/report/student_batch_wise_attendance/student_batch_wise_attendance.py b/erpnext/schools/report/student_batch_wise_attendance/student_batch_wise_attendance.py index ddb4889cbf..646e3f7987 100644 --- a/erpnext/schools/report/student_batch_wise_attendance/student_batch_wise_attendance.py +++ b/erpnext/schools/report/student_batch_wise_attendance/student_batch_wise_attendance.py @@ -38,7 +38,7 @@ def execute(filters=None): def get_columns(filters): columns = [ - _("Student Group") + ":Link/Student Batch:250", + _("Student Group") + ":Link/Student Group:250", _("Student Group Strength") + "::170", _("Present") + "::90", _("Absent") + "::90", diff --git a/erpnext/selling/doctype/installation_note/installation_note.js b/erpnext/selling/doctype/installation_note/installation_note.js index d4b2179172..9aff74ad86 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.js +++ b/erpnext/selling/doctype/installation_note/installation_note.js @@ -42,18 +42,23 @@ erpnext.selling.InstallationNote = frappe.ui.form.Controller.extend({ }, refresh: function() { + var me = this; if (this.frm.doc.docstatus===0) { - cur_frm.add_custom_button(__('From Delivery Note'), + this.frm.add_custom_button(__('From Delivery Note'), function() { erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_installation_note", source_doctype: "Delivery Note", + target: me.frm, + date_field: "posting_date", + setters: { + customer: me.frm.doc.customer || undefined, + }, get_query_filters: { docstatus: 1, status: ["not in", ["Stopped", "Closed"]], per_installed: ["<", 99.99], - customer: cur_frm.doc.customer || undefined, - company: cur_frm.doc.company + company: me.frm.doc.company } }) }, "fa fa-download", "btn-default" diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 9c37365ac4..940daaa507 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -25,6 +25,8 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ refresh: function(doc, dt, dn) { this._super(doc, dt, dn); + var me = this; + if(doc.docstatus == 1 && doc.status!=='Lost') { cur_frm.add_custom_button(__('Make Sales Order'), cur_frm.cscript['Make Sales Order']); @@ -36,17 +38,24 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ } if (this.frm.doc.docstatus===0) { - cur_frm.add_custom_button(__('Opportunity'), + this.frm.add_custom_button(__('Opportunity'), function() { + var setters = {}; + if(me.frm.doc.customer) { + setters.customer = me.frm.doc.customer || undefined; + } else if (me.frm.doc.lead) { + setters.lead = me.frm.doc.lead || undefined; + } erpnext.utils.map_current_doc({ method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation", source_doctype: "Opportunity", + target: me.frm, + setters: setters, get_query_filters: { status: ["not in", ["Lost", "Closed"]], - enquiry_type: cur_frm.doc.order_type, - customer: cur_frm.doc.customer || undefined, - lead: cur_frm.doc.lead || undefined, - company: cur_frm.doc.company + company: me.frm.doc.company, + // cannot set enquiry_type as setter, as the fieldname is order_type + enquiry_type: me.frm.doc.order_type, } }) }, __("Get items from"), "btn-default"); diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index ee194f379f..2f2a7ed815 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, "autoname": "naming_series:", @@ -12,6 +13,7 @@ "editable_grid": 1, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -41,6 +43,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -71,6 +74,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -102,6 +106,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -134,6 +139,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -166,6 +172,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -198,6 +205,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -226,6 +234,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -255,6 +264,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -287,6 +297,7 @@ "width": "150px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -320,6 +331,7 @@ "width": "150px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -352,6 +364,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -384,6 +397,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -415,6 +429,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -444,6 +459,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -474,6 +490,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -506,6 +523,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -534,6 +552,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -562,6 +581,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -591,6 +611,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -620,6 +641,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -649,6 +671,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -677,6 +700,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -710,6 +734,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -740,6 +765,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -769,6 +795,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -801,6 +828,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -834,6 +862,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -862,6 +891,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -894,6 +924,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -923,6 +954,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -953,6 +985,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -981,6 +1014,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1011,6 +1045,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1036,13 +1071,14 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0, "width": "40px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1070,6 +1106,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1100,6 +1137,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1132,6 +1170,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1159,6 +1198,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1189,6 +1229,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1218,6 +1259,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1248,6 +1290,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1279,6 +1322,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1306,6 +1350,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1336,6 +1381,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1363,6 +1409,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1394,6 +1441,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1423,6 +1471,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1450,6 +1499,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1481,6 +1531,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1508,6 +1559,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1537,6 +1589,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -1567,6 +1620,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1598,6 +1652,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1628,6 +1683,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1656,6 +1712,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1685,6 +1742,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1714,6 +1772,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1744,6 +1803,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1776,6 +1836,7 @@ "width": "200px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1808,6 +1869,7 @@ "width": "200px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1840,6 +1902,7 @@ "width": "200px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1869,6 +1932,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1901,6 +1965,7 @@ "width": "200px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -1933,6 +1998,7 @@ "width": "200px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1964,6 +2030,7 @@ "width": "200px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -1995,6 +2062,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2026,6 +2094,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2056,6 +2125,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -2085,6 +2155,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -2116,6 +2187,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -2147,6 +2219,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2175,6 +2248,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2204,6 +2278,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -2234,6 +2309,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2265,6 +2341,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2296,6 +2373,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -2327,6 +2405,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2356,6 +2435,7 @@ "width": "50%" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2388,6 +2468,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2418,6 +2499,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -2448,19 +2530,19 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-shopping-cart", "idx": 82, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2017-02-20 13:22:18.466192", + "modified": "2017-05-15 13:05:39.469590", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index d32fe77d8e..b5b24f8c75 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -102,7 +102,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): target.run_method("calculate_taxes_and_totals") def update_item(obj, target, source_parent): - target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor) + target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor) doclist = get_mapped_doc("Quotation", source_name, { "Quotation": { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 0175976fde..5a4de0bfdc 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -136,12 +136,15 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.quotation.quotation.make_sales_order", source_doctype: "Quotation", + target: me.frm, + setters: { + customer: me.frm.doc.customer || undefined, + order_type: me.frm.doc.order_type, + }, get_query_filters: { + company: me.frm.doc.company, docstatus: 1, status: ["!=", "Lost"], - order_type: me.frm.doc.order_type, - customer: me.frm.doc.customer || undefined, - company: me.frm.doc.company } }) }, __("Get items from")); diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index c18ae0c8ea..0417e5e37c 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -529,8 +529,8 @@ def make_sales_order(**args): "item_code": args.item or args.item_code or "_Test Item", "warehouse": args.warehouse, "qty": args.qty or 10, - "rate": args.rate or 100, - "conversion_factor": 1.0, + "uom": args.uom or None, + "rate": args.rate or 100 }) if not args.do_not_save: diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index 67ba1c8f96..e37e88d2ef 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -163,6 +163,10 @@ erpnext.SalesFunnel = Class.extend({ draw_legend: function(x_mid, y_mid, width, height, title) { var context = this.elements.context; + if(y_mid == 0) { + y_mid = 7; + } + // draw line context.beginPath(); context.moveTo(x_mid, y_mid); diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py index d6e7ea713d..536b72f6a9 100644 --- a/erpnext/setup/doctype/naming_series/naming_series.py +++ b/erpnext/setup/doctype/naming_series/naming_series.py @@ -8,6 +8,7 @@ from frappe.utils import cstr from frappe import msgprint, throw, _ from frappe.model.document import Document +from frappe.model.naming import parse_naming_series from frappe.permissions import get_doctypes_with_read class NamingSeriesNotSetError(frappe.ValidationError): pass @@ -136,8 +137,9 @@ class NamingSeries(Document): def get_current(self, arg=None): """get series current""" if self.prefix: + prefix = self.parse_naming_series() self.current_value = frappe.db.get_value("Series", - self.prefix.split('.')[0], "current", order_by = "name") + prefix, "current", order_by = "name") def insert_series(self, series): """insert series if missing""" @@ -146,7 +148,7 @@ class NamingSeries(Document): def update_series_start(self): if self.prefix: - prefix = self.prefix.split('.')[0] + prefix = self.parse_naming_series() self.insert_series(prefix) frappe.db.sql("update `tabSeries` set current = %s where name = %s", (self.current_value, prefix)) @@ -154,6 +156,17 @@ class NamingSeries(Document): else: msgprint(_("Please select prefix first")) + def parse_naming_series(self): + parts = self.prefix.split('.') + # If series contain date format like INV.YYYY.MM.##### + if len(parts) > 2: + del parts[-1] # Removed ### from the series + prefix = parse_naming_series(parts) + else: + prefix = parts[0] + + return prefix + def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True): from frappe.custom.doctype.property_setter.property_setter import make_property_setter if naming_series: diff --git a/erpnext/setup/doctype/territory/territory.json b/erpnext/setup/doctype/territory/territory.json index 030f9369a5..b123d5e1ac 100644 --- a/erpnext/setup/doctype/territory/territory.json +++ b/erpnext/setup/doctype/territory/territory.json @@ -79,7 +79,7 @@ "bold": 1, "collapsible": 0, "columns": 0, - "description": "Only leaf nodes are allowed in transaction", + "description": "", "fieldname": "is_group", "fieldtype": "Check", "hidden": 0, @@ -478,4 +478,4 @@ "sort_order": "DESC", "track_changes": 0, "track_seen": 0 -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 5236031e6d..eb8a750c14 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -119,13 +119,16 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note", source_doctype: "Sales Order", + target: me.frm, + setters: { + customer: me.frm.doc.customer || undefined, + }, get_query_filters: { docstatus: 1, status: ["!=", "Closed"], per_delivered: ["<", 99.99], + company: me.frm.doc.company, project: me.frm.doc.project || undefined, - customer: me.frm.doc.customer || undefined, - company: me.frm.doc.company } }) }, __("Get items from")); diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 3402619eac..460f1c3deb 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -65,7 +65,8 @@ frappe.ui.form.on("Item", { frm.page.set_inner_btn_group_as_primary(__("Make")); } if (frm.doc.variant_of) { - frm.set_intro(__("This Item is a Variant of {0} (Template). Attributes will be copied over from the template unless 'No Copy' is set", [frm.doc.variant_of]), true); + frm.set_intro(__("This Item is a Variant of {0} (Template). Attributes will be copied over from the template unless 'No Copy' is set", + [frm.doc.variant_of]), true); } if (frappe.defaults.get_default("item_naming_by")!="Naming Series" || frm.doc.variant_of) { @@ -96,6 +97,8 @@ frappe.ui.form.on("Item", { } frappe.set_route('Form', 'Item', new_item.name); }); + + frm.trigger('make_variant_fields_read_only'); }, validate: function(frm){ @@ -106,6 +109,16 @@ frappe.ui.form.on("Item", { refresh_field("image_view"); }, + make_variant_fields_read_only: function(frm) { + if(frm.doc.variant_of) { + frm.meta.fields.forEach(function(df) { + if (!df.no_copy) { + frm.toggle_enable(df.fieldname, false); + } + }); + } + }, + is_fixed_asset: function(frm) { if (frm.doc.is_fixed_asset) { frm.set_value("is_stock_item", 0); diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index d42e60d1bf..25f30552b5 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -513,6 +513,39 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "description": "", + "fieldname": "tolerance", + "fieldtype": "Float", + "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 over delivery or receipt upto this percent", + "length": 0, + "no_copy": 0, + "oldfieldname": "tolerance", + "oldfieldtype": "Currency", + "permlevel": 0, + "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, @@ -800,39 +833,6 @@ "unique": 0, "width": "50%" }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_stock_item", - "description": "", - "fieldname": "tolerance", - "fieldtype": "Float", - "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 over delivery or receipt upto this percent", - "length": 0, - "no_copy": 0, - "oldfieldname": "tolerance", - "oldfieldtype": "Currency", - "permlevel": 0, - "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, @@ -3143,7 +3143,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2017-05-03 09:55:11.624283", + "modified": "2017-05-15 11:49:47.525859", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index f86417135d..ef85dd9a68 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -86,6 +86,7 @@ class Item(WebsiteGenerator): self.validate_has_variants() self.validate_attributes() self.validate_variant_attributes() + self.copy_variant_attributes() self.validate_website_image() self.make_thumbnail() self.validate_fixed_asset() @@ -616,7 +617,9 @@ class Item(WebsiteGenerator): template_item.save() def update_variants(self): - if self.has_variants and not self.flags.dont_update_variants: + if self.flags.dont_update_variants: + return + if self.has_variants: updated = [] variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name }) for d in variants: @@ -625,7 +628,7 @@ class Item(WebsiteGenerator): variant.save() updated.append(d.item_code) if updated: - frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated))) + frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated))) def validate_has_variants(self): if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"): @@ -670,6 +673,12 @@ class Item(WebsiteGenerator): validate_item_variant_attributes(self, args) + def copy_variant_attributes(self): + '''Copy attributes from template (if they have been changed before saving)''' + if self.variant_of: + template = frappe.get_doc('Item', self.variant_of) + copy_attributes_to_variant(template, self) + def get_timeline_data(doctype, name): '''returns timeline data based on stock ledger entry''' out = {} diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index be0b4c2536..58c16e1964 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -95,16 +95,19 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten } if (this.frm.doc.docstatus===0) { - cur_frm.add_custom_button(__('Sales Order'), + this.frm.add_custom_button(__('Sales Order'), function() { erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_material_request", source_doctype: "Sales Order", + target: me.frm, + setters: { + company: me.frm.doc.company + }, get_query_filters: { docstatus: 1, status: ["!=", "Closed"], per_delivered: ["<", 99.99], - company: cur_frm.doc.company } }) }, __("Get items from")); diff --git a/erpnext/stock/doctype/price_list/price_list.js b/erpnext/stock/doctype/price_list/price_list.js index 23ac6fd7a9..c362b5a765 100644 --- a/erpnext/stock/doctype/price_list/price_list.js +++ b/erpnext/stock/doctype/price_list/price_list.js @@ -1,13 +1,14 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -$.extend(cur_frm.cscript, { - refresh: function() { - cur_frm.add_custom_button(__("Add / Edit Prices"), function() { +frappe.ui.form.on("Price List", { + refresh: function(frm) { + let me = this; + frm.add_custom_button(__("Add / Edit Prices"), function() { frappe.route_options = { - "price_list": cur_frm.doc.name + "price_list": frm.doc.name }; frappe.set_route("Report", "Item Price"); }, "fa fa-money"); } -}); +}); \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 383de01e70..5c97e7cd63 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -43,6 +43,7 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend }, refresh: function() { + var me = this; this._super(); if(this.frm.doc.docstatus===1) { this.show_stock_ledger(); @@ -53,17 +54,20 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend if(!this.frm.doc.is_return && this.frm.doc.status!="Closed") { if(this.frm.doc.docstatus==0) { - cur_frm.add_custom_button(__('Purchase Order'), + this.frm.add_custom_button(__('Purchase Order'), function() { erpnext.utils.map_current_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt", source_doctype: "Purchase Order", + target: me.frm, + setters: { + supplier: me.frm.doc.supplier || undefined, + }, get_query_filters: { - supplier: cur_frm.doc.supplier || undefined, docstatus: 1, status: ["!=", "Closed"], per_received: ["<", 99.99], - company: cur_frm.doc.company + company: me.frm.doc.company } }) }, __("Get items from")); diff --git a/erpnext/utilities/__init__.py b/erpnext/utilities/__init__.py index b94061c29c..944f9785a4 100644 --- a/erpnext/utilities/__init__.py +++ b/erpnext/utilities/__init__.py @@ -2,6 +2,7 @@ import frappe from erpnext.utilities.activation import get_level +from frappe.utils import cstr def update_doctypes(): for d in frappe.db.sql("""select df.parent, df.fieldname @@ -26,7 +27,7 @@ def get_site_info(site_info): company = company[0][0] if company else None if company: - domain = frappe.db.get_value('Company', company, 'domain') + domain = frappe.db.get_value('Company', cstr(company), 'domain') return { 'company': company, diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 9a25b76cbe..7657188bb0 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -18,6 +18,10 @@ class TransactionBase(StatusUpdater): frappe.db.get_value("Notification Control", None, dt + "_message")) def validate_posting_time(self): + # set Edit Posting Date and Time to 1 while data import + if frappe.flags.in_import: + self.set_posting_time = 1 + if not getattr(self, 'set_posting_time', None): now = now_datetime() self.posting_date = now.strftime('%Y-%m-%d')