From a0f5ffb7e7f3ac7e0b1d3d611e88711860721999 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 4 Jan 2024 10:48:01 +0530 Subject: [PATCH 1/3] fix: flaky demo test case --- erpnext/setup/demo.py | 215 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 erpnext/setup/demo.py diff --git a/erpnext/setup/demo.py b/erpnext/setup/demo.py new file mode 100644 index 00000000000..df2c49b2b62 --- /dev/null +++ b/erpnext/setup/demo.py @@ -0,0 +1,215 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import json +import os +from random import randint + +import frappe +from frappe import _ +from frappe.utils import add_days, getdate + +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry +from erpnext.accounts.utils import get_fiscal_year +from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice +from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice +from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account + + +def setup_demo_data(): + from frappe.utils.telemetry import capture + + capture("demo_data_creation_started", "erpnext") + try: + company = create_demo_company() + process_masters() + make_transactions(company) + frappe.cache.delete_keys("bootinfo") + frappe.publish_realtime("demo_data_complete") + except Exception: + frappe.log_error("Failed to create demo data") + capture("demo_data_creation_failed", "erpnext", properties={"exception": frappe.get_traceback()}) + raise + capture("demo_data_creation_completed", "erpnext") + + +@frappe.whitelist() +def clear_demo_data(): + from frappe.utils.telemetry import capture + + frappe.only_for("System Manager") + + capture("demo_data_erased", "erpnext") + try: + company = frappe.db.get_single_value("Global Defaults", "demo_company") + create_transaction_deletion_record(company) + clear_masters() + delete_company(company) + default_company = frappe.db.get_single_value("Global Defaults", "default_company") + frappe.db.set_default("company", default_company) + except Exception: + frappe.db.rollback() + frappe.log_error("Failed to erase demo data") + frappe.throw( + _("Failed to erase demo data, please delete the demo company manually."), + title=_("Could Not Delete Demo Data"), + ) + + +def create_demo_company(): + company = frappe.db.get_all("Company")[0].name + company_doc = frappe.get_doc("Company", company) + + # Make a dummy company + new_company = frappe.new_doc("Company") + new_company.company_name = company_doc.company_name + " (Demo)" + new_company.abbr = company_doc.abbr + "D" + new_company.enable_perpetual_inventory = 1 + new_company.default_currency = company_doc.default_currency + new_company.country = company_doc.country + new_company.chart_of_accounts_based_on = "Standard Template" + new_company.chart_of_accounts = company_doc.chart_of_accounts + new_company.insert() + + # Set Demo Company as default to + frappe.db.set_single_value("Global Defaults", "demo_company", new_company.name) + frappe.db.set_default("company", new_company.name) + + bank_account = create_bank_account({"company_name": new_company.name}) + frappe.db.set_value("Company", new_company.name, "default_bank_account", bank_account.name) + + return new_company.name + + +def process_masters(): + for doctype in frappe.get_hooks("demo_master_doctypes"): + data = read_data_file_using_hooks(doctype) + if data: + for item in json.loads(data): + create_demo_record(item) + + +def create_demo_record(doctype): + frappe.get_doc(doctype).insert(ignore_permissions=True) + + +def make_transactions(company): + frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1) + start_date = get_fiscal_year(date=getdate())[1] + + for doctype in frappe.get_hooks("demo_transaction_doctypes"): + data = read_data_file_using_hooks(doctype) + if data: + for item in json.loads(data): + create_transaction(item, company, start_date) + + convert_order_to_invoices() + frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 0) + + +def create_transaction(doctype, company, start_date): + document_type = doctype.get("doctype") + warehouse = get_warehouse(company) + + if document_type == "Purchase Order": + posting_date = get_random_date(start_date, 1, 25) + else: + posting_date = get_random_date(start_date, 31, 350) + + doctype.update( + { + "company": company, + "set_posting_time": 1, + "transaction_date": posting_date, + "schedule_date": posting_date, + "delivery_date": posting_date, + "set_warehouse": warehouse, + } + ) + + doc = frappe.get_doc(doctype) + doc.save(ignore_permissions=True) + doc.submit() + + +def convert_order_to_invoices(): + for document in ["Purchase Order", "Sales Order"]: + # Keep some orders intentionally unbilled/unpaid + for i, order in enumerate( + frappe.db.get_all( + document, filters={"docstatus": 1}, fields=["name", "transaction_date"], limit=6 + ) + ): + + if document == "Purchase Order": + invoice = make_purchase_invoice(order.name) + elif document == "Sales Order": + invoice = make_sales_invoice(order.name) + + invoice.set_posting_time = 1 + invoice.posting_date = order.transaction_date + invoice.due_date = order.transaction_date + invoice.bill_date = order.transaction_date + + if invoice.get("payment_schedule"): + invoice.payment_schedule[0].due_date = order.transaction_date + + invoice.update_stock = 1 + invoice.submit() + + if i % 2 != 0: + payment = get_payment_entry(invoice.doctype, invoice.name) + payment.reference_no = invoice.name + payment.submit() + + +def get_random_date(start_date, start_range, end_range): + return add_days(start_date, randint(start_range, end_range)) + + +def create_transaction_deletion_record(company): + transaction_deletion_record = frappe.new_doc("Transaction Deletion Record") + transaction_deletion_record.company = company + transaction_deletion_record.save(ignore_permissions=True) + transaction_deletion_record.submit() + + +def clear_masters(): + for doctype in frappe.get_hooks("demo_master_doctypes")[::-1]: + data = read_data_file_using_hooks(doctype) + if data: + for item in json.loads(data): + clear_demo_record(item) + + +def clear_demo_record(document): + document_type = document.get("doctype") + del document["doctype"] + + valid_columns = frappe.get_meta(document_type).get_valid_columns() + + filters = document + for key in list(filters): + if key not in valid_columns: + filters.pop(key, None) + + doc = frappe.get_doc(document_type, filters) + doc.delete(ignore_permissions=True) + + +def delete_company(company): + frappe.db.set_single_value("Global Defaults", "demo_company", "") + frappe.delete_doc("Company", company, ignore_permissions=True) + + +def read_data_file_using_hooks(doctype): + path = os.path.join(os.path.dirname(__file__), "demo_data") + with open(os.path.join(path, doctype + ".json"), "r") as f: + data = f.read() + + return data + + +def get_warehouse(company): + warehouses = frappe.db.get_all("Warehouse", {"company": company, "is_group": 0}) + return warehouses[randint(0, 3)].name -- GitLab From e833d96bde4b6c0b220724a51275a390a9bf4659 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 4 Jan 2024 07:03:26 +0000 Subject: [PATCH 2/3] fix: don't keep demo in Dokos --- erpnext/setup/demo.py | 215 ------------------------------------------ 1 file changed, 215 deletions(-) delete mode 100644 erpnext/setup/demo.py diff --git a/erpnext/setup/demo.py b/erpnext/setup/demo.py deleted file mode 100644 index df2c49b2b62..00000000000 --- a/erpnext/setup/demo.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import json -import os -from random import randint - -import frappe -from frappe import _ -from frappe.utils import add_days, getdate - -from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry -from erpnext.accounts.utils import get_fiscal_year -from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice -from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice -from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account - - -def setup_demo_data(): - from frappe.utils.telemetry import capture - - capture("demo_data_creation_started", "erpnext") - try: - company = create_demo_company() - process_masters() - make_transactions(company) - frappe.cache.delete_keys("bootinfo") - frappe.publish_realtime("demo_data_complete") - except Exception: - frappe.log_error("Failed to create demo data") - capture("demo_data_creation_failed", "erpnext", properties={"exception": frappe.get_traceback()}) - raise - capture("demo_data_creation_completed", "erpnext") - - -@frappe.whitelist() -def clear_demo_data(): - from frappe.utils.telemetry import capture - - frappe.only_for("System Manager") - - capture("demo_data_erased", "erpnext") - try: - company = frappe.db.get_single_value("Global Defaults", "demo_company") - create_transaction_deletion_record(company) - clear_masters() - delete_company(company) - default_company = frappe.db.get_single_value("Global Defaults", "default_company") - frappe.db.set_default("company", default_company) - except Exception: - frappe.db.rollback() - frappe.log_error("Failed to erase demo data") - frappe.throw( - _("Failed to erase demo data, please delete the demo company manually."), - title=_("Could Not Delete Demo Data"), - ) - - -def create_demo_company(): - company = frappe.db.get_all("Company")[0].name - company_doc = frappe.get_doc("Company", company) - - # Make a dummy company - new_company = frappe.new_doc("Company") - new_company.company_name = company_doc.company_name + " (Demo)" - new_company.abbr = company_doc.abbr + "D" - new_company.enable_perpetual_inventory = 1 - new_company.default_currency = company_doc.default_currency - new_company.country = company_doc.country - new_company.chart_of_accounts_based_on = "Standard Template" - new_company.chart_of_accounts = company_doc.chart_of_accounts - new_company.insert() - - # Set Demo Company as default to - frappe.db.set_single_value("Global Defaults", "demo_company", new_company.name) - frappe.db.set_default("company", new_company.name) - - bank_account = create_bank_account({"company_name": new_company.name}) - frappe.db.set_value("Company", new_company.name, "default_bank_account", bank_account.name) - - return new_company.name - - -def process_masters(): - for doctype in frappe.get_hooks("demo_master_doctypes"): - data = read_data_file_using_hooks(doctype) - if data: - for item in json.loads(data): - create_demo_record(item) - - -def create_demo_record(doctype): - frappe.get_doc(doctype).insert(ignore_permissions=True) - - -def make_transactions(company): - frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1) - start_date = get_fiscal_year(date=getdate())[1] - - for doctype in frappe.get_hooks("demo_transaction_doctypes"): - data = read_data_file_using_hooks(doctype) - if data: - for item in json.loads(data): - create_transaction(item, company, start_date) - - convert_order_to_invoices() - frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 0) - - -def create_transaction(doctype, company, start_date): - document_type = doctype.get("doctype") - warehouse = get_warehouse(company) - - if document_type == "Purchase Order": - posting_date = get_random_date(start_date, 1, 25) - else: - posting_date = get_random_date(start_date, 31, 350) - - doctype.update( - { - "company": company, - "set_posting_time": 1, - "transaction_date": posting_date, - "schedule_date": posting_date, - "delivery_date": posting_date, - "set_warehouse": warehouse, - } - ) - - doc = frappe.get_doc(doctype) - doc.save(ignore_permissions=True) - doc.submit() - - -def convert_order_to_invoices(): - for document in ["Purchase Order", "Sales Order"]: - # Keep some orders intentionally unbilled/unpaid - for i, order in enumerate( - frappe.db.get_all( - document, filters={"docstatus": 1}, fields=["name", "transaction_date"], limit=6 - ) - ): - - if document == "Purchase Order": - invoice = make_purchase_invoice(order.name) - elif document == "Sales Order": - invoice = make_sales_invoice(order.name) - - invoice.set_posting_time = 1 - invoice.posting_date = order.transaction_date - invoice.due_date = order.transaction_date - invoice.bill_date = order.transaction_date - - if invoice.get("payment_schedule"): - invoice.payment_schedule[0].due_date = order.transaction_date - - invoice.update_stock = 1 - invoice.submit() - - if i % 2 != 0: - payment = get_payment_entry(invoice.doctype, invoice.name) - payment.reference_no = invoice.name - payment.submit() - - -def get_random_date(start_date, start_range, end_range): - return add_days(start_date, randint(start_range, end_range)) - - -def create_transaction_deletion_record(company): - transaction_deletion_record = frappe.new_doc("Transaction Deletion Record") - transaction_deletion_record.company = company - transaction_deletion_record.save(ignore_permissions=True) - transaction_deletion_record.submit() - - -def clear_masters(): - for doctype in frappe.get_hooks("demo_master_doctypes")[::-1]: - data = read_data_file_using_hooks(doctype) - if data: - for item in json.loads(data): - clear_demo_record(item) - - -def clear_demo_record(document): - document_type = document.get("doctype") - del document["doctype"] - - valid_columns = frappe.get_meta(document_type).get_valid_columns() - - filters = document - for key in list(filters): - if key not in valid_columns: - filters.pop(key, None) - - doc = frappe.get_doc(document_type, filters) - doc.delete(ignore_permissions=True) - - -def delete_company(company): - frappe.db.set_single_value("Global Defaults", "demo_company", "") - frappe.delete_doc("Company", company, ignore_permissions=True) - - -def read_data_file_using_hooks(doctype): - path = os.path.join(os.path.dirname(__file__), "demo_data") - with open(os.path.join(path, doctype + ".json"), "r") as f: - data = f.read() - - return data - - -def get_warehouse(company): - warehouses = frappe.db.get_all("Warehouse", {"company": company, "is_group": 0}) - return warehouses[randint(0, 3)].name -- GitLab From c28d4deb7fc92e3e9aa7de06bac284421e80b8d6 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 4 Jan 2024 08:57:55 +0000 Subject: [PATCH 3/3] fix(ContactAddressQuickEntry): Fix mandatory and default df properties --- .../js/utils/contact_address_quick_entry.js | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/erpnext/public/js/utils/contact_address_quick_entry.js b/erpnext/public/js/utils/contact_address_quick_entry.js index 137fbf59879..952ebdfcfac 100644 --- a/erpnext/public/js/utils/contact_address_quick_entry.js +++ b/erpnext/public/js/utils/contact_address_quick_entry.js @@ -1,37 +1,59 @@ frappe.provide('frappe.ui.form'); frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm extends frappe.ui.form.QuickEntryForm { + map_field_names = { + "email_address": "email_id", + "mobile_number": "mobile_no", + }; + constructor(doctype, after_insert, init_callback, doc, force) { super(doctype, after_insert, init_callback, doc, force); this.skip_redirect_on_error = true; } - render_dialog() { - const me = this; + async render_dialog() { this.mandatory = this.mandatory.concat(this.get_variant_fields()); - // @dokos - this.mandatory.map(m => { + for (const m of this.mandatory) { + // @dokos if (m.fieldname == "company_search") { - m.get_query = function() { + m.get_query = () => { return { query: "erpnext.buying.doctype.supplier.supplier.company_query" }; } - m.onchange = function() { - const selection = this._data.filter(f => f.value == me.dialog.get_value("company_search")) + m.onchange = () => { + const selection = this._data.filter(f => f.value == this.dialog.get_value("company_search")) if (selection.length){ const selected_company = selection[0] - me.dialog.set_values({ + this.dialog.set_values({ customer_name: selected_company.label, siren_number: selected_company.value }) } } } - }) + + // Grab the original df from the doctype definition + if (m.fieldname) { + let fieldname = m.fieldname; + fieldname = this.map_field_names[fieldname] ?? fieldname; + + const dt = m._for_doctype ?? this.doctype; + await frappe.model.with_doctype(dt); + + const original_df = frappe.meta.get_docfield(dt, fieldname); + if (original_df) { + const default_value = frappe.defaults.get_default(fieldname); + Object.assign(m, original_df, { + "depends_on": "", + "default": default_value, + }); + } + } + } super.render_dialog(); } @@ -41,12 +63,7 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm * Using alias fieldnames because the doctype definition define "email_id" and "mobile_no" as readonly fields. * Therefor, resulting in the fields being "hidden". */ - const map_field_names = { - "email_address": "email_id", - "mobile_number": "mobile_no", - }; - - Object.entries(map_field_names).forEach(([fieldname, new_fieldname]) => { + Object.entries(this.map_field_names).forEach(([fieldname, new_fieldname]) => { this.dialog.doc[new_fieldname] = this.dialog.doc[fieldname]; delete this.dialog.doc[fieldname]; }); @@ -61,6 +78,7 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm collapsible: 1 }, { + _for_doctype: "Contact", label: __("Email Id"), fieldname: "email_address", fieldtype: "Data", @@ -70,6 +88,7 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm fieldtype: "Column Break" }, { + _for_doctype: "Contact", label: __("Mobile Number"), fieldname: "mobile_number", fieldtype: "Data" @@ -80,16 +99,19 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm collapsible: 1 }, { + _for_doctype: "Address", label: __("Address Line 1"), fieldname: "address_line1", fieldtype: "Data" }, { + _for_doctype: "Address", label: __("Address Line 2"), fieldname: "address_line2", fieldtype: "Data" }, { + _for_doctype: "Address", label: __("ZIP Code"), fieldname: "pincode", fieldtype: "Data" @@ -98,16 +120,19 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm fieldtype: "Column Break" }, { + _for_doctype: "Address", label: __("City"), fieldname: "city", fieldtype: "Data" }, { + _for_doctype: "Address", label: __("State"), fieldname: "state", fieldtype: "Data" }, { + _for_doctype: "Address", label: __("Country"), fieldname: "country", fieldtype: "Link", -- GitLab