diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 895c314510a..0d67752ba74 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -707,6 +707,7 @@ def get_payment_terms_template(party_name, party_type, company=None): if party_type not in ("Customer", "Supplier"): return template = None + if party_type == "Customer": customer = frappe.get_cached_value( "Customer", party_name, fieldname=["payment_terms", "customer_group"], as_dict=1 diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json index c27ede29d1b..dfdae1dec69 100644 --- a/erpnext/accounts/workspace/accounting/accounting.json +++ b/erpnext/accounts/workspace/accounting/accounting.json @@ -5,7 +5,7 @@ "label": "Profit and Loss" } ], - "content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"i0EtSjDAXq\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"X78jcbq1u3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"iAwpe-Chra\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Accounting\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"id\":\"OX7lZHbiTr\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"VVvJ1lUcfc\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Bills\",\"col\":3}},{\"id\":\"Vlj2FZtlHV\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Bills\",\"col\":3}},{\"id\":\"VVVjQVAhPf\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Payment\",\"col\":3}},{\"id\":\"DySNdlysIW\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Payment\",\"col\":3}},{\"id\":\"i0EtSjDAXq\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"X78jcbq1u3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"iAwpe-Chra\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Accounting\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"id\":\"OX7lZHbiTr\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 15:41:59.515192", "custom_blocks": [], "docstatus": 0, @@ -1061,11 +1061,28 @@ "type": "Link" } ], - "modified": "2023-07-04 14:32:15.842044", + "modified": "2023-08-10 17:41:14.059005", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", - "number_cards": [], + "number_cards": [ + { + "label": "Total Outgoing Bills", + "number_card_name": "Total Outgoing Bills" + }, + { + "label": "Total Incoming Bills", + "number_card_name": "Total Incoming Bills" + }, + { + "label": "Total Incoming Payment", + "number_card_name": "Total Incoming Payment" + }, + { + "label": "Total Outgoing Payment", + "number_card_name": "Total Outgoing Payment" + } + ], "owner": "Administrator", "parent_page": "", "public": 1, diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 44c68dcb574..7eaa146db65 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -70,6 +70,19 @@ treeviews = [ "Department", ] +demo_master_doctypes = [ + "item_group", + "item", + "customer_group", + "supplier_group", + "customer", + "supplier", +] +demo_transaction_doctypes = [ + "purchase_order", + "sales_order", +] + jinja = { "methods": [ "erpnext.stock.serial_batch_bundle.get_serial_or_batch_nos", diff --git a/erpnext/public/build.json b/erpnext/public/build.json deleted file mode 100644 index b9b48aba453..00000000000 --- a/erpnext/public/build.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "css/erpnext.css": [ - "public/less/erpnext.less", - "public/scss/call_popup.scss", - "public/scss/point-of-sale.scss" - ], - "js/erpnext-web.min.js": [ - "public/js/website_utils.js", - "public/js/shopping_cart.js", - "public/js/wishlist.js" - ], - "css/erpnext-web.css": [ - "public/scss/website.scss", - "public/scss/shopping_cart.scss" - ], - "js/erpnext.min.js": [ - "public/js/conf.js", - "public/js/utils.js", - "public/js/queries.js", - "public/js/sms_manager.js", - "public/js/utils/party.js", - "public/js/controllers/stock_controller.js", - "public/js/payment/payments.js", - "public/js/controllers/taxes_and_totals.js", - "public/js/controllers/transaction.js", - "public/js/templates/item_selector.html", - "public/js/utils/item_selector.js", - "public/js/help_links.js", - "public/js/templates/item_quick_entry.html", - "public/js/utils/customer_quick_entry.js", - "public/js/utils/supplier_quick_entry.js", - "public/js/education/student_button.html", - "public/js/education/assessment_result_tool.html", - "public/js/call_popup/call_popup.js", - "public/js/utils/dimension_tree_filter.js", - "public/js/telephony.js", - "public/js/templates/call_link.html", - "public/js/bulk_transaction_processing.js" - ], - "js/item-dashboard.min.js": [ - "stock/dashboard/item_dashboard.html", - "stock/dashboard/item_dashboard_list.html", - "stock/dashboard/item_dashboard.js", - "stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html", - "stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html" - ], - "js/point-of-sale.min.js": [ - "selling/page/point_of_sale/pos_item_selector.js", - "selling/page/point_of_sale/pos_item_cart.js", - "selling/page/point_of_sale/pos_item_details.js", - "selling/page/point_of_sale/pos_number_pad.js", - "selling/page/point_of_sale/pos_payment.js", - "selling/page/point_of_sale/pos_past_order_list.js", - "selling/page/point_of_sale/pos_past_order_summary.js", - "selling/page/point_of_sale/pos_controller.js" - ], - "js/bank-reconciliation-tool.min.js": [ - "public/js/bank_reconciliation_tool/data_table_manager.js", - "public/js/bank_reconciliation_tool/number_card.js", - "public/js/bank_reconciliation_tool/dialog_manager.js" - ], - "js/e-commerce.min.js": [ - "e_commerce/product_ui/views.js", - "e_commerce/product_ui/grid.js", - "e_commerce/product_ui/list.js", - "e_commerce/product_ui/search.js" - ] -} diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js index d7bea7b8341..966a9e1f9b3 100644 --- a/erpnext/public/js/erpnext.bundle.js +++ b/erpnext/public/js/erpnext.bundle.js @@ -28,5 +28,6 @@ import "./controllers/accounts.js" import "./utils/landed_taxes_and_charges_common.js"; import "./utils/sales_common.js"; import "./controllers/buying.js"; +import "./utils/demo.js"; // import { sum } from 'frappe/public/utils/util.js' diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 934fd1f88ae..ba200ef1681 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -40,6 +40,12 @@ erpnext.setup.slides_settings = [ { fieldname: 'fy_start_date', label: __('Financial Year Begins On'), fieldtype: 'Date', reqd: 1 }, // end date should be hidden (auto calculated) { fieldname: 'fy_end_date', label: __('End Date'), fieldtype: 'Date', reqd: 1, hidden: 1 }, + { fieldtype: "Section Break" }, + { + fieldname: 'setup_demo', + label: __('Generate Demo Data for Exploration'), + fieldtype: 'Check', + description: 'If checked, we will create demo data for you to explore the system. This demo data can be erased later.'}, ], onload: function (slide) { diff --git a/erpnext/public/js/utils/demo.js b/erpnext/public/js/utils/demo.js new file mode 100644 index 00000000000..05866e98647 --- /dev/null +++ b/erpnext/public/js/utils/demo.js @@ -0,0 +1,71 @@ +$(document).on("toolbar_setup", function () { + if (frappe.boot.sysdefaults.demo_company) { + erpnext.setup_clear_button(); + } + + // for first load + frappe.realtime.on("demo_data_complete", () => { + erpnext.setup_clear_button(); + }); +}); + +erpnext.setup_clear_button = function () { + let message_string = __( + "Demo data is present on the system, erase data before starting real usage." + ); + let $floatingBar = $(` +
+
+

+ ${message_string} +

+ +
+
+ `); + + $("footer").append($floatingBar); + + $("#clear-demo").on("click", function () { + frappe.confirm( + __("Are you sure you want to clear all demo data?"), + () => { + frappe.call({ + method: "erpnext.setup.demo.clear_demo_data", + freeze: true, + freeze_message: __("Clearing Demo Data..."), + callback: function (r) { + frappe.ui.toolbar.clear_cache(); + frappe.show_alert({ + message: __("Demo data cleared"), + indicator: "green", + }); + $("footer").remove($floatingBar); + }, + }); + } + ); + }); +}; diff --git a/erpnext/setup/demo.py b/erpnext/setup/demo.py new file mode 100644 index 00000000000..1c19974fce6 --- /dev/null +++ b/erpnext/setup/demo.py @@ -0,0 +1,202 @@ +# 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, 30) + else: + posting_date = get_random_date(start_date, 31, 365) + + 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.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"] + doc = frappe.get_doc(document_type, document) + frappe.delete_doc(doc.doctype, doc.name, 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 diff --git a/erpnext/setup/demo_data/customer.json b/erpnext/setup/demo_data/customer.json new file mode 100644 index 00000000000..1b47906eb6c --- /dev/null +++ b/erpnext/setup/demo_data/customer.json @@ -0,0 +1,20 @@ +[ + { + "doctype": "Customer", + "customer_group": "Demo Customer Group", + "territory": "All Territories", + "customer_name": "Grant Plastics Ltd." + }, + { + "doctype": "Customer", + "customer_group": "Demo Customer Group", + "territory": "All Territories", + "customer_name": "West View Software Ltd." + }, + { + "doctype": "Customer", + "customer_group": "Demo Customer Group", + "territory": "All Territories", + "customer_name": "Palmer Productions Ltd." + } +] \ No newline at end of file diff --git a/erpnext/setup/demo_data/customer_group.json b/erpnext/setup/demo_data/customer_group.json new file mode 100644 index 00000000000..754333548a8 --- /dev/null +++ b/erpnext/setup/demo_data/customer_group.json @@ -0,0 +1,6 @@ +[ + { + "doctype": "Customer Group", + "customer_group_name": "Demo Customer Group" + } +] \ No newline at end of file diff --git a/erpnext/setup/demo_data/item.json b/erpnext/setup/demo_data/item.json new file mode 100644 index 00000000000..ffe41528af5 --- /dev/null +++ b/erpnext/setup/demo_data/item.json @@ -0,0 +1,72 @@ +[ + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU001", + "item_name": "T-shirt", + "image": "https://images.pexels.com/photos/1484808/pexels-photo-1484808.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU002", + "item_name": "Laptop", + "image": "https://images.pexels.com/photos/3999538/pexels-photo-3999538.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU003", + "item_name": "Book", + "image": "https://images.pexels.com/photos/2422178/pexels-photo-2422178.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU004", + "item_name": "Smartphone", + "image": "https://images.pexels.com/photos/1647976/pexels-photo-1647976.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU005", + "item_name": "Sneakers", + "image": "https://images.pexels.com/photos/1598505/pexels-photo-1598505.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU006", + "item_name": "Coffee Mug", + "image": "https://images.pexels.com/photos/585753/pexels-photo-585753.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU007", + "item_name": "Television", + "image": "https://images.pexels.com/photos/8059376/pexels-photo-8059376.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU008", + "item_name": "Backpack", + "image": "https://images.pexels.com/photos/3731256/pexels-photo-3731256.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU009", + "item_name": "Headphones", + "image": "https://images.pexels.com/photos/3587478/pexels-photo-3587478.jpeg" + }, + { + "doctype": "Item", + "item_group": "Demo Item Group", + "item_code": "SKU010", + "item_name": "Camera", + "image": "https://images.pexels.com/photos/51383/photo-camera-subject-photographer-51383.jpeg" + } +] diff --git a/erpnext/setup/demo_data/item_group.json b/erpnext/setup/demo_data/item_group.json new file mode 100644 index 00000000000..acb261f4ed5 --- /dev/null +++ b/erpnext/setup/demo_data/item_group.json @@ -0,0 +1,7 @@ +[ + { + "doctype": "Item Group", + "item_group_name": "Demo Item Group", + "parent_item_group": "All Item Groups" + } +] \ No newline at end of file diff --git a/erpnext/setup/demo_data/journal_entry.json b/erpnext/setup/demo_data/journal_entry.json new file mode 100644 index 00000000000..b751c7cf244 --- /dev/null +++ b/erpnext/setup/demo_data/journal_entry.json @@ -0,0 +1,25 @@ +[ + { + "cheque_date": "2023-03-14", + "cheque_no": "33", + "doctype": "Journal Entry", + "accounts": [ + { + "party_type": "Customer", + "party": "ABC Enterprises", + "credit_in_account_currency": 40000.0, + "debit_in_account_currency": 0.0, + "doctype": "Journal Entry Account", + "parentfield": "accounts", + }, + { + "credit_in_account_currency": 0.0, + "debit_in_account_currency": 40000.0, + "doctype": "Journal Entry Account", + "parentfield": "accounts", + } + ], + "user_remark": "test", + "voucher_type": "Bank Entry" + } +] \ No newline at end of file diff --git a/erpnext/setup/demo_data/payment_entry.json b/erpnext/setup/demo_data/payment_entry.json new file mode 100644 index 00000000000..c0767c3114d --- /dev/null +++ b/erpnext/setup/demo_data/payment_entry.json @@ -0,0 +1,57 @@ +[ + { + "doctype": "Payment Entry", + "payment_type": "Receive", + "party_type": "Customer", + "party": "ABC Enterprises", + "paid_amount": 67000, + "received_amount": 67000, + "reference_no": "#ref0001", + "source_exchange_rate": 1, + "target_exchange_rate": 1 + }, + { + "doctype": "Payment Entry", + "payment_type": "Receive", + "party_type": "Customer", + "party": "XYZ Corporation", + "paid_amount": 500000, + "received_amount": 500000, + "reference_no": "#ref0001", + "source_exchange_rate": 1, + "target_exchange_rate": 1 + }, + { + "doctype": "Payment Entry", + "payment_type": "Receive", + "party_type": "Customer", + "party": "KJPR Pvt. Ltd.", + "paid_amount": 300000, + "received_amount": 30000, + "reference_no": "#ref0001", + "source_exchange_rate": 1, + "target_exchange_rate": 1 + }, + { + "doctype": "Payment Entry", + "payment_type": "Pay", + "party_type": "Supplier", + "party": "DQ Industries", + "paid_amount": 85000, + "received_amount": 85000, + "reference_no": "#ref0005", + "source_exchange_rate": 1, + "target_exchange_rate": 1 + }, + { + "doctype": "Payment Entry", + "payment_type": "Pay", + "party_type": "Supplier", + "party": "KC Corp.", + "paid_amount": 100000, + "received_amount": 100000, + "reference_no": "#ref0006", + "source_exchange_rate": 1, + "target_exchange_rate": 1 + } +] \ No newline at end of file diff --git a/erpnext/setup/demo_data/purchase_order.json b/erpnext/setup/demo_data/purchase_order.json new file mode 100644 index 00000000000..42ffa8862a9 --- /dev/null +++ b/erpnext/setup/demo_data/purchase_order.json @@ -0,0 +1,162 @@ +[ + { + "conversion_rate": 1.0, + "supplier": "Zuckerman Security Ltd.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU001", + "parentfield": "items", + "qty": 100.0, + "rate": 400.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "MA Inc.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU002", + "parentfield": "items", + "qty": 50.0, + "rate": 300.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "Summit Traders Ltd.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU003", + "parentfield": "items", + "qty": 200.0, + "rate": 523.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "Zuckerman Security Ltd.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU004", + "parentfield": "items", + "qty": 60.0, + "rate": 725.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "MA Inc.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU005", + "parentfield": "items", + "qty": 182.0, + "rate": 222.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "Summit Traders Ltd.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU006", + "parentfield": "items", + "qty": 250.0, + "rate": 420.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "Zuckerman Security Ltd.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU007", + "parentfield": "items", + "qty": 190.0, + "rate": 375.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "MA Inc.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU008", + "parentfield": "items", + "qty": 121.0, + "rate": 333.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "Summit Traders Ltd.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU009", + "parentfield": "items", + "qty": 76.0, + "rate": 700.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "supplier": "Zuckerman Security Ltd.", + "doctype": "Purchase Order", + "update_stock": 1, + "items": [ + { + "doctype": "Purchase Order Item", + "item_code": "SKU010", + "parentfield": "items", + "qty": 78.0, + "rate": 500.0, + "conversion_factor": 1 + } + ] + } +] \ No newline at end of file diff --git a/erpnext/setup/demo_data/sales_order.json b/erpnext/setup/demo_data/sales_order.json new file mode 100644 index 00000000000..d39063723ee --- /dev/null +++ b/erpnext/setup/demo_data/sales_order.json @@ -0,0 +1,122 @@ +[ + { + "conversion_rate": 1.0, + "customer": "Grant Plastics Ltd.", + "doctype": "Sales Order", + "update_stock": 1, + "items": [ + { + "doctype": "Sales Order Item", + "item_code": "SKU004", + "parentfield": "items", + "qty": 20.0, + "rate": 1000.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "customer": "West View Software Ltd.", + "doctype": "Sales Order", + "update_stock": 1, + "items": [ + { + "doctype": "Sales Order Item", + "item_code": "SKU001", + "parentfield": "items", + "qty": 25.0, + "rate": 800.0, + "conversion_factor": 1 + }, + { + "doctype": "Sales Order Item", + "item_code": "SKU002", + "parentfield": "items", + "qty": 15.0, + "rate": 800.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "customer": "West View Software Ltd.", + "doctype": "Sales Order", + "update_stock": 1, + "items": [ + { + "doctype": "Sales Order Item", + "item_code": "SKU003", + "parentfield": "items", + "qty": 100, + "rate": 500.0, + "conversion_factor": 1 + }, + { + "doctype": "Sales Order Item", + "item_code": "SKU006", + "parentfield": "items", + "qty": 100, + "rate": 890.0, + "conversion_factor": 1 + }, + { + "doctype": "Sales Order Item", + "item_code": "SKU007", + "parentfield": "items", + "qty": 100, + "rate": 900.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "customer": "Palmer Productions Ltd.", + "doctype": "Sales Order", + "update_stock": 1, + "items": [ + { + "doctype": "Sales Order Item", + "item_code": "SKU005", + "parentfield": "items", + "qty": 150.0, + "rate": 100.0, + "conversion_factor": 1 + } + ] + }, + { + "conversion_rate": 1.0, + "customer": "Grant Plastics Ltd.", + "doctype": "Sales Order", + "update_stock": 1, + "items": [ + { + "doctype": "Sales Order Item", + "item_code": "SKU008", + "parentfield": "items", + "qty": 20.0, + "rate": 500.0, + "conversion_factor": 1 + }, + { + "doctype": "Sales Order Item", + "item_code": "SKU009", + "parentfield": "items", + "qty": 40.0, + "rate": 300.0, + "conversion_factor": 1 + }, + { + "doctype": "Sales Order Item", + "item_code": "SKU010", + "parentfield": "items", + "qty": 50.0, + "rate": 900.0, + "conversion_factor": 1 + } + ] + } +] \ No newline at end of file diff --git a/erpnext/setup/demo_data/supplier.json b/erpnext/setup/demo_data/supplier.json new file mode 100644 index 00000000000..01a4e8969a4 --- /dev/null +++ b/erpnext/setup/demo_data/supplier.json @@ -0,0 +1,17 @@ +[ + { + "doctype": "Supplier", + "supplier_group": "Demo Supplier Group", + "supplier_name": "Zuckerman Security Ltd." + }, + { + "doctype": "Supplier", + "supplier_group": "Demo Supplier Group", + "supplier_name": "MA Inc." + }, + { + "doctype": "Supplier", + "supplier_group": "Demo Supplier Group", + "supplier_name": "Summit Traders Ltd." + } +] \ No newline at end of file diff --git a/erpnext/setup/demo_data/supplier_group.json b/erpnext/setup/demo_data/supplier_group.json new file mode 100644 index 00000000000..17070bf9b60 --- /dev/null +++ b/erpnext/setup/demo_data/supplier_group.json @@ -0,0 +1,6 @@ +[ + { + "doctype": "Supplier Group", + "supplier_group_name": "Demo Supplier Group" + } +] \ No newline at end of file diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py index fd2fe300fac..babd7dd4e84 100644 --- a/erpnext/setup/doctype/company/test_company.py +++ b/erpnext/setup/doctype/company/test_company.py @@ -195,6 +195,22 @@ class TestCompany(unittest.TestCase): child_company.save() self.test_basic_tree() + def test_demo_data(self): + from erpnext.setup.demo import clear_demo_data, setup_demo_data + + setup_demo_data() + company_name = frappe.db.get_value("Company", {"name": ("like", "%(Demo)")}) + self.assertTrue(company_name) + + for transaction in frappe.get_hooks("demo_transaction_doctypes"): + self.assertTrue(frappe.db.exists(frappe.unscrub(transaction), {"company": company_name})) + + clear_demo_data() + company_name = frappe.db.get_value("Company", {"name": ("like", "%(Demo)")}) + self.assertFalse(company_name) + for transaction in frappe.get_hooks("demo_transaction_doctypes"): + self.assertFalse(frappe.db.exists(frappe.unscrub(transaction), {"company": company_name})) + def create_company_communication(doctype, docname): comm = frappe.get_doc( diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.json b/erpnext/setup/doctype/global_defaults/global_defaults.json index 823d2ba7d7b..bd80e1d8584 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.json +++ b/erpnext/setup/doctype/global_defaults/global_defaults.json @@ -12,7 +12,8 @@ "default_currency", "hide_currency_symbol", "disable_rounded_total", - "disable_in_words" + "disable_in_words", + "demo_company" ], "fields": [ { @@ -71,6 +72,14 @@ "fieldtype": "Check", "in_list_view": 1, "label": "Disable In Words" + }, + { + "fieldname": "demo_company", + "fieldtype": "Link", + "hidden": 1, + "label": "Demo Company", + "options": "Company", + "read_only": 1 } ], "icon": "fa fa-cog", diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 535c87d652c..ae6881b99e0 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -490,7 +490,7 @@ def update_stock_settings(): def create_bank_account(args): if not args.get("bank_account"): - return + args["bank_account"] = _("Bank Account") company_name = args.get("company_name") bank_account_group = frappe.db.get_value( diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py index 65b268e385c..2da107e4e94 100644 --- a/erpnext/setup/setup_wizard/setup_wizard.py +++ b/erpnext/setup/setup_wizard/setup_wizard.py @@ -5,7 +5,8 @@ import frappe from frappe import _ -from .operations import install_fixtures as fixtures +from erpnext.setup.demo import setup_demo_data +from erpnext.setup.setup_wizard.operations import install_fixtures as fixtures def get_setup_stages(args=None): @@ -36,6 +37,11 @@ def get_setup_stages(args=None): {"fn": setup_defaults, "args": args, "fail_msg": _("Failed to setup defaults")}, ], }, + { + "status": _("Setting up demo data"), + "fail_msg": _("Failed to setup demo data"), + "tasks": [{"fn": setup_demo, "args": args, "fail_msg": _("Failed to setup demo data")}], + }, { "status": _("Wrapping up"), "fail_msg": _("Failed to login"), @@ -63,6 +69,11 @@ def fin(args): login_as_first_user(args) +def setup_demo(args): + if args.get("setup_demo"): + frappe.enqueue(setup_demo_data, enqueue_after_commit=True, at_front=True) + + def login_as_first_user(args): if args.get("email") and hasattr(frappe.local, "login_manager"): frappe.local.login_manager.login_as(args.get("email")) diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index db1cc494e0b..bdbf8b4fac5 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -61,6 +61,8 @@ def boot_session(bootinfo): ) bootinfo.party_account_types = frappe._dict(party_account_types) + bootinfo.sysdefaults.demo_company = frappe.db.get_single_value("Global Defaults", "demo_company") + def update_page_info(bootinfo): bootinfo.page_info.update(