diff --git a/.github/helper/.flake8_strict b/.github/helper/.flake8_strict index 4c7f5f82cfb..c8337a9c125 100644 --- a/.github/helper/.flake8_strict +++ b/.github/helper/.flake8_strict @@ -65,6 +65,11 @@ ignore = E713, E712, +enable-extensions = + M90 + +select = + M511 max-line-length = 200 exclude=.github/helper/semgrep_rules,test_*.py diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 658892c20ef..d765f0482c8 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -104,6 +104,8 @@ jobs: - name: Build Assets run: cd ~/frappe-bench/ && bench build + env: + CI: Yes - name: UI Tests run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2b3a471f774..e411f11301e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,10 @@ repos: rev: 3.9.2 hooks: - id: flake8 - args: ['--config', '.github/helper/.flake8_strict'] + additional_dependencies: [ + 'flake8-mutable', + ] + args: ['--select=M511', '--config', '.github/helper/.flake8_strict'] exclude: ".*setup.py$" - repo: https://github.com/timothycrosley/isort diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index b1f3e6fd014..412833bd192 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -52,21 +52,35 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo refresh() { this.frm.disable_save(); + this.frm.set_df_property('invoices', 'cannot_delete_rows', true); + this.frm.set_df_property('payments', 'cannot_delete_rows', true); + this.frm.set_df_property('allocation', 'cannot_delete_rows', true); + + this.frm.set_df_property('invoices', 'cannot_add_rows', true); + this.frm.set_df_property('payments', 'cannot_add_rows', true); + this.frm.set_df_property('allocation', 'cannot_add_rows', true); + if (this.frm.doc.receivable_payable_account) { this.frm.add_custom_button(__('Get Unreconciled Entries'), () => this.frm.trigger("get_unreconciled_entries") ); + this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'primary'); } if (this.frm.doc.invoices.length && this.frm.doc.payments.length) { this.frm.add_custom_button(__('Allocate'), () => this.frm.trigger("allocate") ); + this.frm.change_custom_button_type('Allocate', null, 'primary'); + this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default'); } if (this.frm.doc.allocation.length) { this.frm.add_custom_button(__('Reconcile'), () => this.frm.trigger("reconcile") ); + this.frm.change_custom_button_type('Reconcile', null, 'primary'); + this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default'); + this.frm.change_custom_button_type('Allocate', null, 'default'); } } diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 9023b3646f2..eb0c20f92d9 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -12,15 +12,16 @@ "receivable_payable_account", "col_break1", "from_invoice_date", - "to_invoice_date", - "minimum_invoice_amount", - "maximum_invoice_amount", - "invoice_limit", - "column_break_13", "from_payment_date", - "to_payment_date", + "minimum_invoice_amount", "minimum_payment_amount", + "column_break_11", + "to_invoice_date", + "to_payment_date", + "maximum_invoice_amount", "maximum_payment_amount", + "column_break_13", + "invoice_limit", "payment_limit", "bank_cash_account", "sec_break1", @@ -79,6 +80,7 @@ }, { "depends_on": "eval:(doc.payments).length || (doc.invoices).length", + "description": "If you need to reconcile particular transactions against each other, then please select accordingly. If not, all the transactions will be allocated in FIFO order.", "fieldname": "sec_break1", "fieldtype": "Section Break", "label": "Unreconciled Entries" @@ -163,6 +165,7 @@ "label": "Maximum Payment Amount" }, { + "description": "System will fetch all the entries if limit value is zero.", "fieldname": "payment_limit", "fieldtype": "Int", "label": "Payment Limit" @@ -171,13 +174,17 @@ "fieldname": "maximum_invoice_amount", "fieldtype": "Currency", "label": "Maximum Invoice Amount" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" } ], "hide_toolbar": 1, "icon": "icon-resize-horizontal", "issingle": 1, "links": [], - "modified": "2021-08-30 13:05:51.977861", + "modified": "2021-10-04 20:27:11.114194", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json index b8c65eea847..6a21692c6ac 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json +++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json @@ -14,8 +14,8 @@ "section_break_6", "allocated_amount", "unreconciled_amount", - "amount", "column_break_8", + "amount", "is_advance", "section_break_5", "difference_amount", @@ -127,12 +127,13 @@ "fieldname": "reference_row", "fieldtype": "Data", "hidden": 1, - "label": "Reference Row" + "label": "Reference Row", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2021-09-20 17:23:09.455803", + "modified": "2021-10-06 11:48:59.616562", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Allocation", diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py index 83ecfb47bb5..7c53f4a0b07 100644 --- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py +++ b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py @@ -33,7 +33,9 @@ class TestPOSProfile(unittest.TestCase): frappe.db.sql("delete from `tabPOS Profile`") -def get_customers_list(pos_profile={}): +def get_customers_list(pos_profile=None): + if pos_profile is None: + pos_profile = {} cond = "1=1" customer_groups = [] if pos_profile.get('customer_groups'): diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 12b486e45eb..0637fdaef02 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -398,7 +398,9 @@ def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules): pricing_rules[0].apply_rule_on_other_items = items return pricing_rules -def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): +def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None): + if items is None: + items = [] sum_qty, sum_amt = [0, 0] doctype = doc.get('parenttype') or doc.doctype diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index d09f7dc2da2..f5391ca4cc9 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -69,7 +69,9 @@ class PromotionalScheme(Document): {'promotional_scheme': self.name}): frappe.delete_doc('Pricing Rule', rule.name) -def get_pricing_rules(doc, rules = {}): +def get_pricing_rules(doc, rules=None): + if rules is None: + rules = {} new_doc = [] for child_doc, fields in {'price_discount_slabs': price_discount_fields, 'product_discount_slabs': product_discount_fields}.items(): @@ -78,7 +80,9 @@ def get_pricing_rules(doc, rules = {}): return new_doc -def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): +def _get_pricing_rules(doc, child_doc, discount_fields, rules=None): + if rules is None: + rules = {} new_doc = [] args = get_args_for_pricing_rule(doc) applicable_for = frappe.scrub(doc.get('applicable_for')) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index d5271885b7e..bb8138bfc2e 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -139,9 +139,9 @@ def get_account_type_based_data(company, account_type, period_list, accumulated_ data["total"] = total return data -def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters={}): +def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters=None): cond = "" - filters = frappe._dict(filters) + filters = frappe._dict(filters or {}) if filters.include_default_book_entries: company_fb = frappe.db.get_value("Company", company, 'default_finance_book') diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py index ae498ba57db..be4ee560a51 100644 --- a/erpnext/education/doctype/student/student.py +++ b/erpnext/education/doctype/student/student.py @@ -138,7 +138,9 @@ class Student(Document): enrollment.submit() return enrollment - def enroll_in_course(self, course_name, program_enrollment, enrollment_date=frappe.utils.datetime.datetime.now()): + def enroll_in_course(self, course_name, program_enrollment, enrollment_date=None): + if enrollment_date is None: + enrollment_date = frappe.utils.datetime.datetime.now() try: enrollment = frappe.get_doc({ "doctype": "Course Enrollment", diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py index f46f14d8416..7d1b9916421 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list.py +++ b/erpnext/hr/doctype/holiday_list/holiday_list.py @@ -1,4 +1,3 @@ - # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt @@ -94,9 +93,11 @@ def get_events(start, end, filters=None): update={"allDay": 1}) -def is_holiday(holiday_list, date=today()): +def is_holiday(holiday_list, date=None): """Returns true if the given date is a holiday in the given holiday list """ + if date is None: + date = today() if holiday_list: return bool(frappe.get_all('Holiday List', dict(name=holiday_list, holiday_date=date))) diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py index 69af5c54c3b..05b74a0dde9 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py @@ -139,7 +139,7 @@ def get_shift_type_timing(shift_types): return shift_timing_map -def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None): +def get_employee_shift(employee, for_date=None, consider_default_shift=False, next_shift_direction=None): """Returns a Shift Type for the given employee on the given date. (excluding the holidays) :param employee: Employee for which shift is required. @@ -147,6 +147,8 @@ def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=Fals :param consider_default_shift: If set to true, default shift is taken when no shift assignment is found. :param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date. """ + if for_date is None: + for_date = nowdate() default_shift = frappe.db.get_value('Employee', employee, 'default_shift') shift_type_name = None shift_assignment_details = frappe.db.get_value('Shift Assignment', {'employee':employee, 'start_date':('<=', for_date), 'docstatus': '1', 'status': "Active"}, ['shift_type', 'end_date']) @@ -200,9 +202,11 @@ def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=Fals return get_shift_details(shift_type_name, for_date) -def get_employee_shift_timings(employee, for_timestamp=now_datetime(), consider_default_shift=False): +def get_employee_shift_timings(employee, for_timestamp=None, consider_default_shift=False): """Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee """ + if for_timestamp is None: + for_timestamp = now_datetime() # write and verify a test case for midnight shift. prev_shift = curr_shift = next_shift = None curr_shift = get_employee_shift(employee, for_timestamp.date(), consider_default_shift, 'forward') @@ -220,7 +224,7 @@ def get_employee_shift_timings(employee, for_timestamp=now_datetime(), consider_ return prev_shift, curr_shift, next_shift -def get_shift_details(shift_type_name, for_date=nowdate()): +def get_shift_details(shift_type_name, for_date=None): """Returns Shift Details which contain some additional information as described below. 'shift_details' contains the following keys: 'shift_type' - Object of DocType Shift Type, @@ -234,6 +238,8 @@ def get_shift_details(shift_type_name, for_date=nowdate()): """ if not shift_type_name: return None + if not for_date: + for_date = nowdate() shift_type = frappe.get_doc('Shift Type', shift_type_name) start_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.start_time for_date = for_date + timedelta(days=1) if shift_type.start_time > shift_type.end_time else for_date diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py index 57a92b05871..93cd4e1f629 100644 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py @@ -155,7 +155,11 @@ def get_designation_counts(designation, company): return employee_counts @frappe.whitelist() -def get_active_staffing_plan_details(company, designation, from_date=getdate(nowdate()), to_date=getdate(nowdate())): +def get_active_staffing_plan_details(company, designation, from_date=None, to_date=None): + if from_date is None: + from_date = getdate(nowdate()) + if to_date is None: + to_date = getdate(nowdate()) if not company or not designation: frappe.throw(_("Please select Company and Designation")) diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 9ed6686d483..178cd5c9d08 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -141,7 +141,6 @@ class TestSalarySlip(unittest.TestCase): create_salary_structure_assignment, ) - no_of_days = self.get_no_of_days() # Payroll based on attendance frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance") @@ -168,9 +167,6 @@ class TestSalarySlip(unittest.TestCase): ss = make_salary_slip_for_payment_days_dependency_test("test_payment_days_based_component@salary.com", salary_structure.name) self.assertEqual(ss.absent_days, 1) - days_in_month = no_of_days[0] - no_of_holidays = no_of_days[1] - ss.reload() payment_days_based_comp_amount = 0 for component in ss.earnings: @@ -992,13 +988,14 @@ def make_salary_structure_for_payment_days_based_component_dependency(): return salary_structure_doc def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure): - employee = frappe.db.get_value("Employee", { - "user_id": employee - }, + employee = frappe.db.get_value( + "Employee", + {"user_id": employee}, ["name", "company", "employee_name"], - as_dict=True) + as_dict=True + ) - salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": employee})}) + salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": employee.name}) if not salary_slip_name: salary_slip = make_salary_slip(salary_structure, employee=employee.name) @@ -1009,4 +1006,4 @@ def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure else: salary_slip = frappe.get_doc("Salary Slip", salary_slip_name) - return salary_slip \ No newline at end of file + return salary_slip diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js index d0c935f4887..f3dc9b74f5a 100644 --- a/erpnext/public/js/help_links.js +++ b/erpnext/public/js/help_links.js @@ -300,7 +300,7 @@ frappe.help.help_links["List/Sales Order"] = [ }, { label: "Recurring Sales Order", - url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices", + url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices", }, { label: "Applying Discount", @@ -397,7 +397,7 @@ frappe.help.help_links["List/Purchase Order"] = [ }, { label: "Recurring Purchase Order", - url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices", + url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices", }, ]; @@ -420,7 +420,7 @@ frappe.help.help_links["Form/Purchase Order"] = [ }, { label: "Recurring Purchase Order", - url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices", + url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices", }, { label: "Subcontracting", @@ -552,7 +552,7 @@ frappe.help.help_links["Form/Sales Invoice"] = [ }, { label: "Accounts Opening Balance", - url: docsUrl + "user/manual/en/accounts/opening-accounts", + url: docsUrl + "user/manual/en/accounts/opening-balance", }, { label: "Sales Return", @@ -604,11 +604,11 @@ frappe.help.help_links["List/Purchase Invoice"] = [ }, { label: "Accounts Opening Balance", - url: docsUrl + "user/manual/en/accounts/opening-accounts", + url: docsUrl + "user/manual/en/accounts/opening-balance", }, { label: "Recurring Purchase Invoice", - url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices", + url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices", }, ]; @@ -623,7 +623,7 @@ frappe.help.help_links["List/Journal Entry"] = [ }, { label: "Accounts Opening Balance", - url: docsUrl + "user/manual/en/accounts/opening-accounts", + url: docsUrl + "user/manual/en/accounts/opening-balance", }, ]; diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 7f39b990bf0..0323a426f0e 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -712,6 +712,7 @@ erpnext.utils.map_current_doc = function(opts) { allow_child_item_selection: opts.allow_child_item_selection, child_fieldname: opts.child_fielname, child_columns: opts.child_columns, + size: opts.size, action: function(selections, args) { let values = selections; if (values.length === 0) { diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 8403193df53..95ca3867ee7 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -46,43 +46,6 @@ frappe.ui.form.on("Company", { }); }, - change_abbreviation(frm) { - var dialog = new frappe.ui.Dialog({ - title: "Replace Abbr", - fields: [ - {"fieldtype": "Data", "label": "New Abbreviation", "fieldname": "new_abbr", - "reqd": 1 }, - {"fieldtype": "Button", "label": "Update", "fieldname": "update"}, - ] - }); - - dialog.fields_dict.update.$input.click(function() { - var args = dialog.get_values(); - if (!args) return; - frappe.show_alert(__("Update in progress. It might take a while.")); - return frappe.call({ - method: "erpnext.setup.doctype.company.company.enqueue_replace_abbr", - args: { - "company": frm.doc.name, - "old": frm.doc.abbr, - "new": args.new_abbr - }, - callback: function(r) { - if (r.exc) { - frappe.msgprint(__("There were errors.")); - return; - } else { - frm.set_value("abbr", args.new_abbr); - } - dialog.hide(); - frm.refresh(); - }, - btn: this - }); - }); - dialog.show(); - }, - company_name: function(frm) { if(frm.doc.__islocal) { // add missing " " arg in split method @@ -164,10 +127,6 @@ frappe.ui.form.on("Company", { }, __('Manage')); } } - - frm.add_custom_button(__('Change Abbreviation'), () => { - frm.trigger('change_abbreviation'); - }, __('Manage')); } erpnext.company.set_chart_of_accounts_options(frm.doc); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 58cb52c04dd..63d96bf85e7 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -125,7 +125,8 @@ "label": "Abbr", "oldfieldname": "abbr", "oldfieldtype": "Data", - "reqd": 1 + "reqd": 1, + "set_only_once": 1 }, { "bold": 1, @@ -747,10 +748,11 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2021-07-12 11:27:06.353860", + "modified": "2021-10-04 12:09:25.833133", "modified_by": "Administrator", "module": "Setup", "name": "Company", + "naming_rule": "By fieldname", "nsm_parent_field": "parent_company", "owner": "Administrator", "permissions": [ @@ -808,4 +810,4 @@ "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 87d67a5f9d0..0b1b4a1ec02 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -399,44 +399,6 @@ class Company(NestedSet): if not frappe.db.get_value('GL Entry', {'company': self.name}): frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name) -@frappe.whitelist() -def enqueue_replace_abbr(company, old, new): - kwargs = dict(queue="long", company=company, old=old, new=new) - frappe.enqueue('erpnext.setup.doctype.company.company.replace_abbr', **kwargs) - - -@frappe.whitelist() -def replace_abbr(company, old, new): - new = new.strip() - if not new: - frappe.throw(_("Abbr can not be blank or space")) - - frappe.only_for("System Manager") - - def _rename_record(doc): - parts = doc[0].rsplit(" - ", 1) - if len(parts) == 1 or parts[1].lower() == old.lower(): - frappe.rename_doc(dt, doc[0], parts[0] + " - " + new, force=True) - - def _rename_records(dt): - # rename is expensive so let's be economical with memory usage - doc = (d for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company)) - for d in doc: - _rename_record(d) - try: - frappe.db.auto_commit_on_many_writes = 1 - for dt in ["Warehouse", "Account", "Cost Center", "Department", - "Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]: - _rename_records(dt) - frappe.db.commit() - frappe.db.set_value("Company", company, "abbr", new) - - except Exception: - frappe.log_error(title=_('Abbreviation Rename Error')) - finally: - frappe.db.auto_commit_on_many_writes = 0 - - def get_name_with_abbr(name, company): company_abbr = frappe.get_cached_value('Company', company, "abbr") parts = name.split(" - ") diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index cf98b19e7a1..17df9777b19 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -296,7 +296,7 @@ def make_purchase_order(source_name, target_doc=None, args=None): return d.ordered_qty < d.stock_qty and child_filter - doclist = get_mapped_doc("Material Request", source_name, { + doclist = get_mapped_doc("Material Request", source_name, { "Material Request": { "doctype": "Purchase Order", "validation": { @@ -323,7 +323,7 @@ def make_purchase_order(source_name, target_doc=None, args=None): @frappe.whitelist() def make_request_for_quotation(source_name, target_doc=None): - doclist = get_mapped_doc("Material Request", source_name, { + doclist = get_mapped_doc("Material Request", source_name, { "Material Request": { "doctype": "Request for Quotation", "validation": { diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 82d8aaed5b3..a9254fb9ecf 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -611,7 +611,9 @@ def get_pos_reserved_serial_nos(filters): return reserved_sr_nos -def fetch_serial_numbers(filters, qty, do_not_include=[]): +def fetch_serial_numbers(filters, qty, do_not_include=None): + if do_not_include is None: + do_not_include = [] batch_join_selection = "" batch_no_condition = "" batch_nos = filters.get("batch_no") diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 19597c3d993..cbff2149d64 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -382,7 +382,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): return out -def get_item_warehouse(item, args, overwrite_warehouse, defaults={}): +def get_item_warehouse(item, args, overwrite_warehouse, defaults=None): if not defaults: defaults = frappe._dict({ 'item_defaults' : get_item_defaults(item.name, args.company), diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index 3cd4cd27617..7c6fbfd9cd1 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import json +from math import ceil import frappe from frappe import _ @@ -149,11 +150,16 @@ def create_material_request(material_requests): conversion_factor = frappe.db.get_value("UOM Conversion Detail", {'parent': item.name, 'uom': uom}, 'conversion_factor') or 1.0 + must_be_whole_number = frappe.db.get_value("UOM", uom, "must_be_whole_number", cache=True) + qty = d.reorder_qty / conversion_factor + if must_be_whole_number: + qty = ceil(qty) + mr.append("items", { "doctype": "Material Request Item", "item_code": d.item_code, "schedule_date": add_days(nowdate(),cint(item.lead_time_days)), - "qty": d.reorder_qty / conversion_factor, + "qty": qty, "uom": uom, "stock_uom": item.stock_uom, "warehouse": d.warehouse,