diff --git a/.travis.yml b/.travis.yml index d6fc930471..a2c0e47349 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ install: - pip install flake8==3.3.0 - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics - sudo rm /etc/apt/sources.list.d/docker.list + - sudo apt-get install hhvm && rm -rf /home/travis/.kiex/ - sudo apt-get purge -y mysql-common mysql-server mysql-client - nvm install v7.10.0 - wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8a247cc745..540e223376 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '10.1.27' +__version__ = '10.1.28' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 5e67e4423f..98d5880571 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -82,7 +82,7 @@ class JournalEntry(AccountsController): d.reference_type = '' d.reference_name = '' d.db_update() - + def unlink_asset_reference(self): for d in self.get("accounts"): if d.reference_type=="Asset" and d.reference_name: @@ -125,7 +125,7 @@ class JournalEntry(AccountsController): if (d.party_type == 'Customer' and flt(d.credit) > 0) or \ (d.party_type == 'Supplier' and flt(d.debit) > 0): if d.is_advance=="No": - msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account)) + msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account), alert=1) elif d.reference_type in ("Sales Order", "Purchase Order") and d.is_advance != "Yes": frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx)) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index 930375d491..f726ec6b80 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -138,7 +138,8 @@ class OpeningInvoiceCreationTool(Document): income_expense_account_field = "expense_account" item = get_item_dict() - return frappe._dict({ + + args = frappe._dict({ "items": [item], "is_opening": "Yes", "set_posting_time": 1, @@ -150,6 +151,11 @@ class OpeningInvoiceCreationTool(Document): "currency": frappe.db.get_value("Company", self.company, "default_currency") }) + if self.invoice_type == "Sales": + args["is_pos"] = 0 + + return args + @frappe.whitelist() def get_temporary_opening_account(company=None): if not company: diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index 5b68f1d2ab..9fefc03716 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -19,7 +19,13 @@

{%= __(report.report_name) %}

-

{%= filters.customer || filters.supplier %}

+

+ {% if (filters.customer_name) { %} + {%= filters.customer_name %} + {% } else { %} + {%= filters.customer || filters.supplier %} + {% } %} +

{% if (filters.tax_id) { %} {%= __("Tax Id: ")%} {%= filters.tax_id %} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index f44ea448e7..3f9d167864 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -17,8 +17,9 @@ frappe.query_reports["Accounts Receivable"] = { "options": "Customer", on_change: () => { var customer = frappe.query_report_filters_by_name.customer.get_value(); - frappe.db.get_value('Customer', customer, "tax_id", function(value) { + frappe.db.get_value('Customer', customer, ["tax_id", "customer_name"], function(value) { frappe.query_report_filters_by_name.tax_id.set_value(value["tax_id"]); + frappe.query_report_filters_by_name.customer_name.set_value(value["customer_name"]); }); } }, @@ -81,6 +82,12 @@ frappe.query_reports["Accounts Receivable"] = { "label": __("Tax Id"), "fieldtype": "Data", "hidden": 1 + }, + { + "fieldname":"customer_name", + "label": __("Customer Name"), + "fieldtype": "Data", + "hidden": 1 } ], diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index ab189cf1af..62295d4a69 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -83,15 +83,15 @@ def validate_returned_items(doc): else: ref = valid_items.get(d.item_code, frappe._dict()) validate_quantity(doc, d, ref, valid_items, already_returned_items) - + if ref.rate and doc.doctype in ("Delivery Note", "Sales Invoice") and flt(d.rate) > ref.rate: frappe.throw(_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}") .format(d.idx, doc.doctype, doc.return_against)) - + elif ref.batch_no and d.batch_no not in ref.batch_no: frappe.throw(_("Row # {0}: Batch No must be same as {1} {2}") .format(d.idx, doc.doctype, doc.return_against)) - + elif ref.serial_no: if not d.serial_no: frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx)) @@ -120,25 +120,30 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items): for column in fields: returned_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0 - reference_qty = (ref.get(column) if column == 'stock_qty' - else ref.get(column) * ref.get("conversion_factor", 1.0)) + + if column == 'stock_qty': + reference_qty = ref.get(column) + current_stock_qty = args.get(column) + else: + reference_qty = ref.get(column) * ref.get("conversion_factor", 1.0) + current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0) max_returnable_qty = flt(reference_qty) - returned_qty label = column.replace('_', ' ').title() - if reference_qty: + if reference_qty: if flt(args.get(column)) > 0: frappe.throw(_("{0} must be negative in return document").format(label)) elif returned_qty >= reference_qty and args.get(column): frappe.throw(_("Item {0} has already been returned") .format(args.item_code), StockOverReturnError) - elif (abs(args.get(column)) * args.get("conversion_factor", 1.0)) > max_returnable_qty: + elif abs(current_stock_qty) > max_returnable_qty: frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}") .format(args.idx, reference_qty, args.item_code), StockOverReturnError) def get_ref_item_dict(valid_items, ref_item_row): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos - + valid_items.setdefault(ref_item_row.item_code, frappe._dict({ "qty": 0, "rate": 0, @@ -160,10 +165,10 @@ def get_ref_item_dict(valid_items, ref_item_row): if ref_item_row.get("serial_no"): item_dict["serial_no"] += get_serial_nos(ref_item_row.serial_no) - + if ref_item_row.get("batch_no"): item_dict["batch_no"].append(ref_item_row.batch_no) - + return valid_items def get_already_returned_items(doc): diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 699e1c608f..e968e61d74 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -29,6 +29,7 @@ class calculate_taxes_and_totals(object): self.set_item_wise_tax_breakup() def _calculate(self): + self.validate_conversion_rate() self.calculate_item_values() self.initialize_taxes() self.determine_exclusive_rate() diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js index a99a919406..cec53563a6 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.js +++ b/erpnext/hr/doctype/salary_slip/salary_slip.js @@ -29,9 +29,10 @@ frappe.ui.form.on("Salary Slip", { }) }, - start_date: function(frm){ + start_date: function(frm, dt, dn){ if(frm.doc.start_date){ frm.trigger("set_end_date"); + get_emp_and_leave_details(frm.doc, dt, dn); } }, @@ -65,18 +66,20 @@ frappe.ui.form.on("Salary Slip", { cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false); }, - salary_slip_based_on_timesheet: function(frm) { + salary_slip_based_on_timesheet: function(frm, dt, dn) { frm.trigger("toggle_fields"); - frm.set_value('start_date', ''); + get_emp_and_leave_details(frm.doc, dt, dn); }, - payroll_frequency: function(frm) { + payroll_frequency: function(frm, dt, dn) { frm.trigger("toggle_fields"); + frm.set_value('end_date', ''); frm.set_value('start_date', ''); + get_emp_and_leave_details(frm.doc, dt, dn); }, - employee: function(frm){ - frm.set_value('start_date', ''); + employee: function(frm, dt, dn) { + get_emp_and_leave_details(frm.doc, dt, dn); }, toggle_fields: function(frm) { @@ -109,7 +112,7 @@ frappe.ui.form.on('Salary Slip Timesheet', { // Get leave details //--------------------------------------------------------------------- -cur_frm.cscript.start_date = function(doc, dt, dn){ +var get_emp_and_leave_details = function(doc, dt, dn) { if(!doc.start_date){ return frappe.call({ method: 'get_emp_and_leave_details', @@ -122,11 +125,9 @@ cur_frm.cscript.start_date = function(doc, dt, dn){ } } -cur_frm.cscript.payroll_frequency = cur_frm.cscript.salary_slip_based_on_timesheet = cur_frm.cscript.start_date; - cur_frm.cscript.employee = function(doc,dt,dn){ doc.salary_structure = '' - cur_frm.cscript.start_date(doc, dt, dn) + get_emp_and_leave_details(doc, dt, dn); } cur_frm.cscript.leave_without_pay = function(doc,dt,dn){ diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index 8e6b69bd9e..bdf3c22bc1 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -161,6 +161,28 @@ frappe.ui.form.on('Salary Structure', { } }); +frappe.ui.form.on('Salary Structure Employee', { + from_date: function(frm, cdt, cdn) { + validate_date(frm, cdt, cdn); + }, + to_date: function(frm, cdt, cdn) { + validate_date(frm, cdt, cdn); + } +}); + +var validate_date = function(frm, cdt, cdn) { + var doc = locals[cdt][cdn]; + if(doc.to_date && doc.from_date) { + var from_date = frappe.datetime.str_to_obj(doc.from_date); + var to_date = frappe.datetime.str_to_obj(doc.to_date); + + if(to_date < from_date) { + frappe.model.set_value(cdt, cdn, "to_date", ""); + frappe.throw(__("From Date cannot be greater than To Date")); + } + } +} + cur_frm.cscript.amount = function(doc, cdt, cdn){ calculate_totals(doc, cdt, cdn); diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index d3b7c5a498..158c4433d5 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -64,7 +64,7 @@ class ProductionOrder(Document): so.name, so_item.delivery_date, so.project from `tabSales Order` so, `tabSales Order Item` so_item, `tabPacked Item` packed_item - where so.name=%s + where so.name=%s and so.name=so_item.parent and so.name=packed_item.parent and so_item.item_code = packed_item.parent_item @@ -88,7 +88,7 @@ class ProductionOrder(Document): self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse") if not self.fg_warehouse: self.fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse") - + def validate_warehouse_belongs_to_company(self): warehouses = [self.fg_warehouse, self.wip_warehouse] for d in self.get("required_items"): @@ -524,7 +524,7 @@ def get_item_details(item, project = None): if not res["bom_no"]: if project: res = get_item_details(item) - frappe.msgprint(_("Default BOM not found for Item {0} and Project {1}").format(item, project)) + frappe.msgprint(_("Default BOM not found for Item {0} and Project {1}").format(item, project), alert=1) else: frappe.throw(_("Default BOM for {0} not found").format(item)) @@ -642,5 +642,5 @@ def query_sales_order(production_item): select distinct so.name from `tabSales Order` so, `tabPacked Item` pi_item where pi_item.parent=so.name and pi_item.item_code=%s and so.docstatus=1 """, (production_item, production_item)) - + return out diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 8812d8e821..e56c0912e8 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -122,6 +122,7 @@ class Company(Document): if not wh_detail["is_group"] else "" }) warehouse.flags.ignore_permissions = True + warehouse.flags.ignore_mandatory = True warehouse.insert() def create_default_accounts(self): diff --git a/erpnext/setup/setup_wizard/operations/company_setup.py b/erpnext/setup/setup_wizard/operations/company_setup.py index 7fe7f0c2bc..7f9795bcfd 100644 --- a/erpnext/setup/setup_wizard/operations/company_setup.py +++ b/erpnext/setup/setup_wizard/operations/company_setup.py @@ -113,8 +113,7 @@ def create_logo(args): " {1}".format(fileurl, args.get("company_name") )) def create_website(args): - if args.get('setup_website'): - website_maker(args) + website_maker(args) def get_fy_details(fy_start_date, fy_end_date): start_year = getdate(fy_start_date).year diff --git a/erpnext/setup/setup_wizard/operations/default_website.py b/erpnext/setup/setup_wizard/operations/default_website.py index d13767648d..8ca213b1a0 100644 --- a/erpnext/setup/setup_wizard/operations/default_website.py +++ b/erpnext/setup/setup_wizard/operations/default_website.py @@ -12,7 +12,7 @@ class website_maker(object): self.args = args self.company = args.company_name self.tagline = args.company_tagline - self.user = args.name + self.user = args.get('email') self.make_web_page() self.make_website_settings() self.make_blog() @@ -50,6 +50,17 @@ class website_maker(object): website_settings.save() def make_blog(self): + blog_category = frappe.get_doc({ + "doctype": "Blog Category", + "category_name": "general", + "published": 1, + "title": _("General") + }).insert() + + if not self.user: + # Admin setup + return + blogger = frappe.new_doc("Blogger") user = frappe.get_doc("User", self.user) blogger.user = self.user @@ -58,13 +69,6 @@ class website_maker(object): blogger.avatar = user.user_image blogger.insert() - blog_category = frappe.get_doc({ - "doctype": "Blog Category", - "category_name": "general", - "published": 1, - "title": _("General") - }).insert() - frappe.get_doc({ "doctype": "Blog Post", "title": "Welcome", diff --git a/erpnext/templates/pages/home.html b/erpnext/templates/pages/home.html index 750fa3849f..f36b4e0817 100644 --- a/erpnext/templates/pages/home.html +++ b/erpnext/templates/pages/home.html @@ -18,7 +18,8 @@
- {{ product_image_square(item.thumbnail or item.image) }} + + {{ product_image_square(item.image) }}
{{ item.item_name }}