diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json index 5c19091c3f..ed8ff7c0f7 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json @@ -110,13 +110,12 @@ "description": "Reference number of the invoice from the previous system", "fieldname": "invoice_number", "fieldtype": "Data", - "in_list_view": 1, "label": "Invoice Number" } ], "istable": 1, "links": [], - "modified": "2021-12-17 19:25:06.053187", + "modified": "2022-03-21 19:31:45.382656", "modified_by": "Administrator", "module": "Accounts", "name": "Opening Invoice Creation Tool Item", @@ -125,5 +124,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 91c07ade7f..d782cc295c 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -485,16 +485,15 @@ class POSInvoice(SalesInvoice): "payment_account": pay.account, }, ["name"]) - args = { - 'doctype': 'Payment Request', + filters = { 'reference_doctype': 'POS Invoice', 'reference_name': self.name, 'payment_gateway_account': payment_gateway_account, 'email_to': self.contact_mobile } - pr = frappe.db.exists(args) + pr = frappe.db.get_value('Payment Request', filters=filters) if pr: - return frappe.get_doc('Payment Request', pr[0][0]) + return frappe.get_doc('Payment Request', pr) @frappe.whitelist() def get_stock_availability(item_code, warehouse): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index e107912fd2..94334892f0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1711,6 +1711,7 @@ def make_delivery_note(source_name, target_doc=None): } }, target_doc, set_missing_values) + doclist.set_onload('ignore_price_list', True) return doclist @frappe.whitelist() diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 2c6654285f..f93f9feb88 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -442,6 +442,8 @@ def make_purchase_receipt(source_name, target_doc=None): } }, target_doc, set_missing_values) + doc.set_onload('ignore_price_list', True) + return doc @frappe.whitelist() @@ -509,6 +511,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions doc = get_mapped_doc("Purchase Order", source_name, fields, target_doc, postprocess, ignore_permissions=ignore_permissions) + doc.set_onload('ignore_price_list', True) return doc diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index 81fc32424e..d5788638f7 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -139,6 +139,7 @@ def make_purchase_order(source_name, target_doc=None): }, }, target_doc, set_missing_values) + doclist.set_onload('ignore_price_list', True) return doclist @frappe.whitelist() diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index dd9b45cc3f..d870823ad1 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -168,7 +168,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): {account_type_condition} AND is_group = 0 AND company = %(company)s - AND account_currency = %(currency)s + AND (account_currency = %(currency)s or ifnull(account_currency, '') = '') AND `{searchfield}` LIKE %(txt)s {mcond} ORDER BY idx DESC, name diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py index 20fb987c60..88a9c10131 100644 --- a/erpnext/crm/doctype/appointment/appointment.py +++ b/erpnext/crm/doctype/appointment/appointment.py @@ -225,9 +225,7 @@ def _check_agent_availability(agent_email, scheduled_time): def _get_employee_from_user(user): - employee_docname = frappe.db.exists( - {'doctype': 'Employee', 'user_id': user}) + employee_docname = frappe.db.get_value('Employee', {'user_id': user}) if employee_docname: - # frappe.db.exists returns a tuple of a tuple - return frappe.get_doc('Employee', employee_docname[0][0]) + return frappe.get_doc('Employee', employee_docname) return None diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py index f4086dc37c..776e604333 100644 --- a/erpnext/crm/doctype/appointment/test_appointment.py +++ b/erpnext/crm/doctype/appointment/test_appointment.py @@ -8,50 +8,44 @@ import frappe def create_test_lead(): - test_lead = frappe.db.exists({'doctype': 'Lead', 'email_id':'test@example.com'}) - if test_lead: - return frappe.get_doc('Lead', test_lead[0][0]) - test_lead = frappe.get_doc({ - 'doctype': 'Lead', - 'lead_name': 'Test Lead', - 'email_id': 'test@example.com' - }) - test_lead.insert(ignore_permissions=True) - return test_lead + test_lead = frappe.db.get_value("Lead", {"email_id": "test@example.com"}) + if test_lead: + return frappe.get_doc("Lead", test_lead) + test_lead = frappe.get_doc( + {"doctype": "Lead", "lead_name": "Test Lead", "email_id": "test@example.com"} + ) + test_lead.insert(ignore_permissions=True) + return test_lead def create_test_appointments(): - test_appointment = frappe.db.exists( - {'doctype': 'Appointment', 'scheduled_time':datetime.datetime.now(),'email':'test@example.com'}) - if test_appointment: - return frappe.get_doc('Appointment', test_appointment[0][0]) - test_appointment = frappe.get_doc({ - 'doctype': 'Appointment', - 'email': 'test@example.com', - 'status': 'Open', - 'customer_name': 'Test Lead', - 'customer_phone_number': '666', - 'customer_skype': 'test', - 'customer_email': 'test@example.com', - 'scheduled_time': datetime.datetime.now() - }) - test_appointment.insert() - return test_appointment + test_appointment = frappe.get_doc( + { + "doctype": "Appointment", + "email": "test@example.com", + "status": "Open", + "customer_name": "Test Lead", + "customer_phone_number": "666", + "customer_skype": "test", + "customer_email": "test@example.com", + "scheduled_time": datetime.datetime.now(), + } + ) + test_appointment.insert() + return test_appointment class TestAppointment(unittest.TestCase): - test_appointment = test_lead = None + test_appointment = test_lead = None - def setUp(self): - self.test_lead = create_test_lead() - self.test_appointment = create_test_appointments() + def setUp(self): + self.test_lead = create_test_lead() + self.test_appointment = create_test_appointments() - def test_calendar_event_created(self): - cal_event = frappe.get_doc( - 'Event', self.test_appointment.calendar_event) - self.assertEqual(cal_event.starts_on, - self.test_appointment.scheduled_time) + def test_calendar_event_created(self): + cal_event = frappe.get_doc("Event", self.test_appointment.calendar_event) + self.assertEqual(cal_event.starts_on, self.test_appointment.scheduled_time) - def test_lead_linked(self): - lead = frappe.get_doc('Lead', self.test_lead.name) - self.assertIsNotNone(lead) + def test_lead_linked(self): + lead = frappe.get_doc("Lead", self.test_lead.name) + self.assertIsNotNone(lead) diff --git a/erpnext/e_commerce/product_ui/views.js b/erpnext/e_commerce/product_ui/views.js index 6dce79dd72..fb63b21a08 100644 --- a/erpnext/e_commerce/product_ui/views.js +++ b/erpnext/e_commerce/product_ui/views.js @@ -418,6 +418,22 @@ erpnext.ProductView = class { me.change_route_with_filters(); }); + + // bind filter lookup input box + $('.filter-lookup-input').on('keydown', frappe.utils.debounce((e) => { + const $input = $(e.target); + const keyword = ($input.val() || '').toLowerCase(); + const $filter_options = $input.next('.filter-options'); + + $filter_options.find('.filter-lookup-wrapper').show(); + $filter_options.find('.filter-lookup-wrapper').each((i, el) => { + const $el = $(el); + const value = $el.data('value').toLowerCase(); + if (!value.includes(keyword)) { + $el.hide(); + } + }); + }, 300)); } change_route_with_filters() { diff --git a/erpnext/education/setup.py b/erpnext/education/setup.py index b716926176..663f1cab4f 100644 --- a/erpnext/education/setup.py +++ b/erpnext/education/setup.py @@ -3,7 +3,6 @@ import frappe -from erpnext.setup.utils import insert_record def setup_education(): @@ -13,6 +12,21 @@ def setup_education(): return create_academic_sessions() + +def insert_record(records): + for r in records: + doc = frappe.new_doc(r.get("doctype")) + doc.update(r) + try: + doc.insert(ignore_permissions=True) + except frappe.DuplicateEntryError as e: + # pass DuplicateEntryError and continue + if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name: + # make sure DuplicateEntryError is for the exact same doc and not a related doc + pass + else: + raise + def create_academic_sessions(): data = [ {"doctype": "Academic Year", "academic_year_name": "2015-16"}, diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 7d32fd8865..3a30990268 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -25,6 +25,7 @@ from erpnext.hr.doctype.leave_application.leave_application import ( LeaveDayBlockedError, NotAnOptionalHoliday, OverlapError, + get_leave_allocation_records, get_leave_balance_on, get_leave_details, ) @@ -882,6 +883,27 @@ class TestLeaveApplication(unittest.TestCase): self.assertEqual(leave_allocation['leaves_pending_approval'], 1) self.assertEqual(leave_allocation['remaining_leaves'], 26) + @set_holiday_list('Salary Slip Test Holiday List', '_Test Company') + def test_get_leave_allocation_records(self): + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + expire_carry_forwarded_leaves_after_days=90) + leave_type.insert() + + leave_alloc = create_carry_forwarded_allocation(employee, leave_type) + details = get_leave_allocation_records(employee.name, getdate(), leave_type.name) + expected_data = { + "from_date": getdate(leave_alloc.from_date), + "to_date": getdate(leave_alloc.to_date), + "total_leaves_allocated": 30.0, + "unused_leaves": 15.0, + "new_leaves_allocated": 15.0, + "leave_type": leave_type.name + } + self.assertEqual(details.get(leave_type.name), expected_data) + def create_carry_forwarded_allocation(employee, leave_type): # initial leave allocation @@ -903,6 +925,8 @@ def create_carry_forwarded_allocation(employee, leave_type): carry_forward=1) leave_allocation.submit() + return leave_allocation + def make_allocation_record(employee=None, leave_type=None, from_date=None, to_date=None, carry_forward=False, leaves=None): allocation = frappe.get_doc({ "doctype": "Leave Allocation", @@ -931,12 +955,9 @@ def set_leave_approver(): dept_doc.save(ignore_permissions=True) def get_leave_period(): - leave_period_name = frappe.db.exists({ - "doctype": "Leave Period", - "company": "_Test Company" - }) + leave_period_name = frappe.db.get_value("Leave Period", {"company": "_Test Company"}) if leave_period_name: - return frappe.get_doc("Leave Period", leave_period_name[0][0]) + return frappe.get_doc("Leave Period", leave_period_name) else: return frappe.get_doc(dict( name = 'Test Leave Period', diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 5f492d7cf2..960d0e5152 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -500,6 +500,9 @@ class JobCard(Document): 2: "Cancelled" }[self.docstatus or 0] + if self.for_quantity <= self.transferred_qty: + self.status = 'Material Transferred' + if self.time_logs: self.status = 'Work In Progress' @@ -507,10 +510,6 @@ class JobCard(Document): (self.for_quantity <= self.total_completed_qty or not self.items)): self.status = 'Completed' - if self.status != 'Completed': - if self.for_quantity <= self.transferred_qty: - self.status = 'Material Transferred' - if update_status: self.db_set('status', self.status) diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index 33425d2314..c5841c16f2 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -169,6 +169,7 @@ class TestJobCard(FrappeTestCase): job_card_name = frappe.db.get_value("Job Card", {'work_order': self.work_order.name}) job_card = frappe.get_doc("Job Card", job_card_name) + self.assertEqual(job_card.status, "Open") # fully transfer both RMs transfer_entry_1 = make_stock_entry_from_jc(job_card_name) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 7eb40ec660..8d7ab85b66 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1150,6 +1150,10 @@ def create_job_card(work_order, row, enable_capacity_planning=False, auto_create doc.insert() frappe.msgprint(_("Job card {0} created").format(get_link_to_form("Job Card", doc.name)), alert=True) + if enable_capacity_planning: + # automatically added scheduling rows shouldn't change status to WIP + doc.db_set("status", "Open") + return doc def get_work_order_operation_data(work_order, operation, workstation): diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 32b0f0f20c..9061c5f1ee 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -708,6 +708,8 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr if not_submitted_ss: frappe.msgprint(_("Could not submit some Salary Slips")) + frappe.flags.via_payroll_entry = False + @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters): diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 5e41b661f8..f0721357d3 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -38,6 +38,8 @@ from erpnext.payroll.doctype.salary_structure.salary_structure import make_salar class TestSalarySlip(unittest.TestCase): def setUp(self): setup_test() + frappe.flags.pop("via_payroll_entry", None) + def tearDown(self): frappe.db.rollback() @@ -409,15 +411,17 @@ class TestSalarySlip(unittest.TestCase): "email_salary_slip_to_employee": 1 }) def test_email_salary_slip(self): - frappe.db.sql("delete from `tabEmail Queue`") + frappe.db.delete("Email Queue") - make_employee("test_email_salary_slip@salary.com", company="_Test Company") - ss = make_employee_salary_slip("test_email_salary_slip@salary.com", "Monthly", "Test Salary Slip Email") + user_id = "test_email_salary_slip@salary.com" + + make_employee(user_id, company="_Test Company") + ss = make_employee_salary_slip(user_id, "Monthly", "Test Salary Slip Email") ss.company = "_Test Company" ss.save() ss.submit() - email_queue = frappe.db.sql("""select name from `tabEmail Queue`""") + email_queue = frappe.db.a_row_exists("Email Queue") self.assertTrue(email_queue) def test_loan_repayment_salary_slip(self): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 00373a6513..43ee5b31c7 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1070,7 +1070,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } if(flt(this.frm.doc.conversion_rate)>0.0) { - if(this.frm.doc.ignore_pricing_rule) { + if(this.frm.doc.__onload && this.frm.doc.__onload.ignore_price_list) { this.calculate_taxes_and_totals(); } else if (!this.in_apply_price_list){ this.apply_price_list(); @@ -1884,6 +1884,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe callback: function(r) { if(!r.exc) { item.item_tax_rate = r.message; + me.add_taxes_from_item_tax_template(item.item_tax_rate); me.calculate_taxes_and_totals(); } } diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 81ff351d37..f484545983 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -608,6 +608,11 @@ function check_can_calculate_pending_qty(me) { && doc.fg_completed_qty && erpnext.stock.bom && erpnext.stock.bom.name === doc.bom_no; - const itemChecks = !!item && !item.allow_alternative_item; + const itemChecks = !!item + && !item.allow_alternative_item + && erpnext.stock.bom && erpnext.stock.items + && (item.item_code in erpnext.stock.bom.items); return docChecks && itemChecks; } + +//# sourceURL=serial_no_batch_selector.js diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index 019496d295..6ae464d2c2 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -264,6 +264,15 @@ body.product-page { font-size: 13px; } + .filter-lookup-input { + background-color: white; + border: 1px solid var(--gray-300); + + &:focus { + border: 1px solid var(--primary); + } + } + .filter-label { font-size: 11px; font-weight: 600; diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 06b4ff18bf..d602f0ca94 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -206,6 +206,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): }, target_doc, set_missing_values, ignore_permissions=ignore_permissions) # postprocess: fetch shipping address, set missing values + doclist.set_onload('ignore_price_list', True) return doclist @@ -269,6 +270,8 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): } }, target_doc, set_missing_values, ignore_permissions=ignore_permissions) + doclist.set_onload('ignore_price_list', True) + return doclist def _make_customer(source_name, ignore_permissions=False): diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 73e3d193d0..b906ec0631 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -584,6 +584,8 @@ def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False): target_doc = get_mapped_doc("Sales Order", source_name, mapper, target_doc, set_missing_values) + target_doc.set_onload('ignore_price_list', True) + return target_doc @frappe.whitelist() @@ -664,6 +666,8 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): if automatically_fetch_payment_terms: doclist.set_payment_schedule() + doclist.set_onload('ignore_price_list', True) + return doclist @frappe.whitelist() diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 1d7bad2686..1d95ddb203 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -21,9 +21,7 @@ default_mail_footer = """
\nThis is a notification for a task that is due today, and a sample Notification. In ERPNext you can setup notifications on anything, Invoices, Orders, Leads, Opportunities, so you never miss a thing.\n
To edit this, and setup other alerts, just type Notification in the search bar.