diff --git a/cleanup_rename.py b/cleanup_rename.py new file mode 100644 index 0000000000..eb5eb93025 --- /dev/null +++ b/cleanup_rename.py @@ -0,0 +1,32 @@ + +import os + +target_dir = os.path.abspath("shop") + +replacements = { + "webshop_settings": "shop_settings", + "Webshop": "Shop", + "webshop_cart": "shop_cart", + "webshop-web.bundle": "shop-web.bundle" +} + +print(f"Scanning {target_dir}...") + +for root, dirs, files in os.walk(target_dir): + for f_name in files: + if f_name.endswith(('.py', '.js', '.json', '.html', '.css', '.scss', '.md', '.txt')): + f_path = os.path.join(root, f_name) + try: + with open(f_path, 'r', encoding='utf-8') as f: + content = f.read() + + new_content = content + for old, new in replacements.items(): + new_content = new_content.replace(old, new) + + if new_content != content: + print(f"Modifying content in {f_name}") + with open(f_path, 'w', encoding='utf-8') as f: + f.write(new_content) + except Exception as e: + print(f"Skipping {f_path}: {e}") diff --git a/pyproject.toml b/pyproject.toml index acc3fbe02d..446f3b0163 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "webshop" +name = "shop" authors = [ { name = "Frappe Technologies Pvt. Ltd.", email = "contact@frappe.io"} ] diff --git a/rename_app.py b/rename_app.py new file mode 100644 index 0000000000..c4e429be6f --- /dev/null +++ b/rename_app.py @@ -0,0 +1,62 @@ + +import os + +target_dir = os.path.abspath("shop") + +# Content Replacements +replacements = { + "webshop.webshop": "shop.shop", + # 'app_name = "webshop"': 'app_name = "shop"', # Handled + "from webshop": "from shop", + "import webshop": "import shop", + '"module": "Webshop"': '"module": "Shop"', + "webshop.patches": "shop.patches", + "webshop-web.bundle.css": "shop-web.bundle.css", + "webshop-web.bundle.scss": "shop-web.bundle.scss", + "webshop_cart.scss": "shop_cart.scss", + # Javascript namespace + "frappe.provide(\"webshop.webshop": "frappe.provide(\"shop.shop", + "webshop.webshop.shopping_cart": "shop.shop.shopping_cart", + "webshop.webshop.": "shop.shop." +} + +print(f"Scanning {target_dir}...") + +# 1. Replace Content +for root, dirs, files in os.walk(target_dir): + for f_name in files: + if f_name.endswith(('.py', '.js', '.json', '.html', '.css', '.scss', '.md', '.txt')): + f_path = os.path.join(root, f_name) + try: + with open(f_path, 'r', encoding='utf-8') as f: + content = f.read() + + new_content = content + for old, new in replacements.items(): + new_content = new_content.replace(old, new) + + if new_content != content: + print(f"Modifying content in {f_name}") + with open(f_path, 'w', encoding='utf-8') as f: + f.write(new_content) + except Exception as e: + print(f"Skipping {f_path}: {e}") + +# 2. Rename Files and Directories +# Process bottom-up to handle children before parents +for root, dirs, files in os.walk(target_dir, topdown=False): + for f_name in files: + if "webshop" in f_name: + new_name = f_name.replace("webshop", "shop") + old_path = os.path.join(root, f_name) + new_path = os.path.join(root, new_name) + print(f"Renaming file {old_path} -> {new_path}") + os.rename(old_path, new_path) + + for d_name in dirs: + if "webshop" in d_name: + new_name = d_name.replace("webshop", "shop") + old_path = os.path.join(root, d_name) + new_path = os.path.join(root, new_name) + print(f"Renaming directory {old_path} -> {new_path}") + os.rename(old_path, new_path) diff --git a/webshop/__init__.py b/shop/__init__.py similarity index 100% rename from webshop/__init__.py rename to shop/__init__.py diff --git a/webshop/config/__init__.py b/shop/config/__init__.py similarity index 100% rename from webshop/config/__init__.py rename to shop/config/__init__.py diff --git a/shop/hooks.py b/shop/hooks.py new file mode 100644 index 0000000000..679b58157c --- /dev/null +++ b/shop/hooks.py @@ -0,0 +1,78 @@ +from . import __version__ as _version + +app_name = "shop" +app_title = "Shop" +app_publisher = "Frappe Technologies Pvt. Ltd." +app_description = "Open Source eCommerce Platform" +app_email = "contact@frappe.io" +app_license = "GNU General Public License (v3)" +app_version = _version + +required_apps = ["payments", "erpnext"] + +web_include_css = "shop-web.bundle.css" + +web_include_js = "web.bundle.js" + +after_install = "shop.setup.install.after_install" +on_logout = "shop.shop.shopping_cart.utils.clear_cart_count" +on_session_creation = [ + "shop.shop.utils.portal.update_debtors_account", + "shop.shop.shopping_cart.utils.set_cart_count", +] +update_website_context = [ + "shop.shop.shopping_cart.utils.update_website_context", +] + +website_generators = ["Website Item", "Item Group"] + +override_doctype_class = { + "Payment Request": "shop.shop.doctype.override_doctype.payment_request.PaymentRequest", + "Item Group": "shop.shop.doctype.override_doctype.item_group.ShopItemGroup", + "Item": "shop.shop.doctype.override_doctype.item.ShopItem", +} + +doctype_js = { + "Item": "public/js/override/item.js", + "Homepage": "public/js/override/homepage.js", +} + +doc_events = { + "Item": { + "on_update": [ + "shop.shop.crud_events.item.update_website_item.execute", + "shop.shop.crud_events.item.invalidate_item_variants_cache.execute", + ], + "before_rename": [ + "shop.shop.crud_events.item.validate_duplicate_website_item.execute", + ], + "after_rename": [ + "shop.shop.crud_events.item.invalidate_item_variants_cache.execute", + ], + }, + "Sales Taxes and Charges Template": { + "on_update": [ + "shop.shop.doctype.shop_settings.shop_settings.validate_cart_settings", + ], + }, + "Quotation": { + "validate": [ + "shop.shop.crud_events.quotation.validate_shopping_cart_items.execute", + ], + }, + "Price List": { + "validate": [ + "shop.shop.crud_events.price_list.check_impact_on_cart.execute" + ], + }, + "Tax Rule": { + "validate": [ + "shop.shop.crud_events.tax_rule.validate_use_for_cart.execute", + ], + }, +} + +has_website_permission = { + "Website Item": "shop.shop.doctype.website_item.website_item.has_website_permission_for_website_item", + "Item Group": "shop.shop.doctype.website_item.website_item.has_website_permission_for_item_group" +} diff --git a/shop/modules.txt b/shop/modules.txt new file mode 100644 index 0000000000..fc0949ee69 --- /dev/null +++ b/shop/modules.txt @@ -0,0 +1 @@ +Shop \ No newline at end of file diff --git a/shop/patches.txt b/shop/patches.txt new file mode 100644 index 0000000000..72d480fb42 --- /dev/null +++ b/shop/patches.txt @@ -0,0 +1,9 @@ +[pre_model_sync] + +[post_model_sync] + +shop.patches.add_homepage_field #09-05-2024 +shop.patches.enable_allow_to_guest_view_for_item_group +shop.patches.clear_cache_for_item_group_route +shop.patches.add_shipping_rule_state_field +shop.patches.add_upi_fields_to_sales_order \ No newline at end of file diff --git a/webshop/patches/__init__.py b/shop/patches/__init__.py similarity index 100% rename from webshop/patches/__init__.py rename to shop/patches/__init__.py diff --git a/webshop/patches/add_homepage_field.py b/shop/patches/add_homepage_field.py similarity index 100% rename from webshop/patches/add_homepage_field.py rename to shop/patches/add_homepage_field.py diff --git a/shop/patches/add_shipping_rule_state_field.py b/shop/patches/add_shipping_rule_state_field.py new file mode 100644 index 0000000000..cf43b02983 --- /dev/null +++ b/shop/patches/add_shipping_rule_state_field.py @@ -0,0 +1,6 @@ +import frappe +from shop.setup.install import add_custom_fields + +def execute(): + frappe.reload_doc("shop", "doctype", "shipping_rule_state") + add_custom_fields() diff --git a/shop/patches/add_upi_fields_to_sales_order.py b/shop/patches/add_upi_fields_to_sales_order.py new file mode 100644 index 0000000000..e4cf04434b --- /dev/null +++ b/shop/patches/add_upi_fields_to_sales_order.py @@ -0,0 +1,46 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + +def execute(): + """Add UPI payment tracking fields to Sales Order.""" + custom_fields = { + "Sales Order": [ + { + "fieldname": "upi_payment_section", + "fieldtype": "Section Break", + "label": "UPI Payment Details", + "insert_after": "payment_schedule", + "collapsible": 1, + }, + { + "fieldname": "upi_transaction_id", + "fieldtype": "Data", + "label": "UPI Transaction ID", + "insert_after": "upi_payment_section", + "read_only": 1, + }, + { + "fieldname": "upi_payment_date", + "fieldtype": "Datetime", + "label": "UPI Payment Date", + "insert_after": "upi_transaction_id", + "read_only": 1, + }, + { + "fieldname": "column_break_upi", + "fieldtype": "Column Break", + "insert_after": "upi_payment_date", + }, + { + "fieldname": "upi_payment_status", + "fieldtype": "Select", + "label": "UPI Payment Status", + "options": "\nPending\nPaid", + "insert_after": "column_break_upi", + "read_only": 1, + "in_list_view": 1, + }, + ] + } + + create_custom_fields(custom_fields) diff --git a/webshop/patches/clear_cache_for_item_group_route.py b/shop/patches/clear_cache_for_item_group_route.py similarity index 100% rename from webshop/patches/clear_cache_for_item_group_route.py rename to shop/patches/clear_cache_for_item_group_route.py diff --git a/webshop/patches/convert_to_website_item_in_item_card_group_template.py b/shop/patches/convert_to_website_item_in_item_card_group_template.py similarity index 90% rename from webshop/patches/convert_to_website_item_in_item_card_group_template.py rename to shop/patches/convert_to_website_item_in_item_card_group_template.py index 40ae1979f9..1d28032c78 100644 --- a/webshop/patches/convert_to_website_item_in_item_card_group_template.py +++ b/shop/patches/convert_to_website_item_in_item_card_group_template.py @@ -3,7 +3,7 @@ import frappe -from webshop.webshop.doctype.website_item.website_item import make_website_item +from shop.shop.doctype.website_item.website_item import make_website_item def execute(): @@ -11,7 +11,7 @@ def execute(): Convert all Item links to Website Item link values in exisitng 'Item Card Group' Web Page Block data. """ - frappe.reload_doc("webshop", "web_template", "item_card_group") + frappe.reload_doc("shop", "web_template", "item_card_group") blocks = frappe.db.get_all( "Web Page Block", diff --git a/webshop/patches/copy_custom_field_filters_to_website_item.py b/shop/patches/copy_custom_field_filters_to_website_item.py similarity index 96% rename from webshop/patches/copy_custom_field_filters_to_website_item.py rename to shop/patches/copy_custom_field_filters_to_website_item.py index 12157cc292..d667b893bb 100644 --- a/webshop/patches/copy_custom_field_filters_to_website_item.py +++ b/shop/patches/copy_custom_field_filters_to_website_item.py @@ -1,7 +1,7 @@ import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field -from webshop.webshop.utils.setup import has_ecommerce_fields +from shop.shop.utils.setup import has_ecommerce_fields def execute(): "Add Field Filters, that are not standard fields in Website Item, as Custom Fields." @@ -42,7 +42,7 @@ def get_table_multiselect_data(docfield): return table_multiselect_data - settings_doctype = "E Commerce Settings" if has_ecommerce_fields() else "Webshop Settings" + settings_doctype = "E Commerce Settings" if has_ecommerce_fields() else "Shop Settings" settings = frappe.get_doc(settings_doctype) diff --git a/webshop/patches/create_website_items.py b/shop/patches/create_website_items.py similarity index 85% rename from webshop/patches/create_website_items.py rename to shop/patches/create_website_items.py index cdec364da2..f5c6ad85b8 100644 --- a/webshop/patches/create_website_items.py +++ b/shop/patches/create_website_items.py @@ -1,17 +1,17 @@ import frappe -from webshop.webshop.doctype.website_item.website_item import make_website_item +from shop.shop.doctype.website_item.website_item import make_website_item def execute(): if frappe.get_all("Website Item", limit=1): return - frappe.reload_doc("webshop", "doctype", "website_item") - frappe.reload_doc("webshop", "doctype", "website_item_tabbed_section") - frappe.reload_doc("webshop", "doctype", "website_offer") - frappe.reload_doc("webshop", "doctype", "recommended_items") - frappe.reload_doc("webshop", "doctype", "webshop_settings") + frappe.reload_doc("shop", "doctype", "website_item") + frappe.reload_doc("shop", "doctype", "website_item_tabbed_section") + frappe.reload_doc("shop", "doctype", "website_offer") + frappe.reload_doc("shop", "doctype", "recommended_items") + frappe.reload_doc("shop", "doctype", "shop_settings") frappe.reload_doc("stock", "doctype", "item") item_fields = [ diff --git a/webshop/patches/enable_allow_to_guest_view_for_item_group.py b/shop/patches/enable_allow_to_guest_view_for_item_group.py similarity index 100% rename from webshop/patches/enable_allow_to_guest_view_for_item_group.py rename to shop/patches/enable_allow_to_guest_view_for_item_group.py diff --git a/webshop/patches/fetch_thumbnail_in_website_items.py b/shop/patches/fetch_thumbnail_in_website_items.py similarity index 100% rename from webshop/patches/fetch_thumbnail_in_website_items.py rename to shop/patches/fetch_thumbnail_in_website_items.py diff --git a/webshop/patches/make_homepage_products_website_items.py b/shop/patches/make_homepage_products_website_items.py similarity index 100% rename from webshop/patches/make_homepage_products_website_items.py rename to shop/patches/make_homepage_products_website_items.py diff --git a/webshop/patches/populate_e_commerce_settings.py b/shop/patches/populate_e_commerce_settings.py similarity index 92% rename from webshop/patches/populate_e_commerce_settings.py rename to shop/patches/populate_e_commerce_settings.py index f5fa326964..ba75270fda 100644 --- a/webshop/patches/populate_e_commerce_settings.py +++ b/shop/patches/populate_e_commerce_settings.py @@ -1,10 +1,10 @@ import frappe from frappe.utils import cint -from webshop.webshop.utils.setup import has_ecommerce_fields +from shop.shop.utils.setup import has_ecommerce_fields def execute(): - frappe.reload_doc("webshop", "doctype", "webshop_settings") + frappe.reload_doc("shop", "doctype", "shop_settings") frappe.reload_doc("portal", "doctype", "website_filter_field") frappe.reload_doc("portal", "doctype", "website_attribute") @@ -35,7 +35,7 @@ def execute(): "save_quotations_as_draft", ] - settings_doctype = "E Commerce Settings" if has_ecommerce_fields() else "Webshop Settings" + settings_doctype = "E Commerce Settings" if has_ecommerce_fields() else "Shop Settings" settings = frappe.get_doc(settings_doctype) diff --git a/webshop/patches/shopping_cart_to_ecommerce.py b/shop/patches/shopping_cart_to_ecommerce.py similarity index 100% rename from webshop/patches/shopping_cart_to_ecommerce.py rename to shop/patches/shopping_cart_to_ecommerce.py diff --git a/webshop/public/.gitkeep b/shop/public/.gitkeep similarity index 100% rename from webshop/public/.gitkeep rename to shop/public/.gitkeep diff --git a/webshop/public/images/cart-empty-state.png b/shop/public/images/cart-empty-state.png similarity index 100% rename from webshop/public/images/cart-empty-state.png rename to shop/public/images/cart-empty-state.png diff --git a/webshop/public/js/customer_reviews.js b/shop/public/js/customer_reviews.js similarity index 95% rename from webshop/public/js/customer_reviews.js rename to shop/public/js/customer_reviews.js index bbef63883e..9168df0110 100644 --- a/webshop/public/js/customer_reviews.js +++ b/shop/public/js/customer_reviews.js @@ -28,7 +28,7 @@ $(() => { primary_action: function() { let data = d.get_values(); frappe.call({ - method: "webshop.webshop.doctype.item_review.item_review.add_item_review", + method: "shop.shop.doctype.item_review.item_review.add_item_review", args: { web_item: $btn.attr('data-web-item'), title: data.title, @@ -66,7 +66,7 @@ $(() => { let me = this; frappe.call({ - method: "webshop.webshop.doctype.item_review.item_review.get_item_reviews", + method: "shop.shop.doctype.item_review.item_review.get_item_reviews", args: { web_item: $btn.attr('data-web-item'), start: me.start, diff --git a/shop/public/js/init.js b/shop/public/js/init.js new file mode 100644 index 0000000000..fb160dea79 --- /dev/null +++ b/shop/public/js/init.js @@ -0,0 +1,2 @@ +if (!window.shop) window.shop = {} +if (!frappe.boot) frappe.boot = {} diff --git a/webshop/public/js/override/homepage.js b/shop/public/js/override/homepage.js similarity index 100% rename from webshop/public/js/override/homepage.js rename to shop/public/js/override/homepage.js diff --git a/webshop/public/js/override/item.js b/shop/public/js/override/item.js similarity index 92% rename from webshop/public/js/override/item.js rename to shop/public/js/override/item.js index e61f122209..7c4afaf2da 100644 --- a/webshop/public/js/override/item.js +++ b/shop/public/js/override/item.js @@ -4,7 +4,7 @@ frappe.ui.form.on("Item", { if (!frm.doc.published_in_website) { frm.add_custom_button(__("Publish in Website"), function() { frappe.call({ - method: "webshop.webshop.doctype.website_item.website_item.make_website_item", + method: "shop.shop.doctype.website_item.website_item.make_website_item", args: { doc: frm.doc, }, diff --git a/webshop/public/js/product_ui/grid.js b/shop/public/js/product_ui/grid.js similarity index 98% rename from webshop/public/js/product_ui/grid.js rename to shop/public/js/product_ui/grid.js index 0212d823a2..68d42ceb7e 100644 --- a/webshop/public/js/product_ui/grid.js +++ b/shop/public/js/product_ui/grid.js @@ -1,7 +1,7 @@ -webshop.ProductGrid = class { +shop.ProductGrid = class { /* Options: - items: Items - - settings: Webshop Settings + - settings: Shop Settings - products_section: Products Wrapper - preference: If preference is not grid view, render but hide */ diff --git a/webshop/public/js/product_ui/list.js b/shop/public/js/product_ui/list.js similarity index 69% rename from webshop/public/js/product_ui/list.js rename to shop/public/js/product_ui/list.js index b4df29d8f2..2b5e134456 100644 --- a/webshop/public/js/product_ui/list.js +++ b/shop/public/js/product_ui/list.js @@ -1,7 +1,7 @@ -webshop.ProductList = class { +shop.ProductList = class { /* Options: - items: Items - - settings: Webshop Settings + - settings: Shop Settings - products_section: Products Wrapper - preference: If preference is not list view, render but hide */ @@ -22,7 +22,7 @@ webshop.ProductList = class { this.items.forEach(item => { let title = item.web_item_name || item.item_name || item.item_code || ""; - title = title.length > 200 ? title.substr(0, 200) + "..." : title; + title = title.length > 200 ? title.substr(0, 200) + "..." : title; html += `
`; html += me.get_image_html(item, title, me.settings); @@ -42,23 +42,23 @@ webshop.ProductList = class { if (image) { image_html += `
- - ${ title } + + ${title} - ${ wishlist_enabled ? this.get_wishlist_icon(item): '' } + ${wishlist_enabled ? this.get_wishlist_icon(item) : ''}
`; } else { image_html += `
-
- ${ frappe.get_abbr(title) } + ${frappe.get_abbr(title)}
- ${ wishlist_enabled ? this.get_wishlist_icon(item): '' } + ${wishlist_enabled ? this.get_wishlist_icon(item) : ''}
`; } @@ -78,9 +78,9 @@ webshop.ProductList = class { let title_html = `
`; title_html += ` `; @@ -98,22 +98,22 @@ webshop.ProductList = class { get_item_details(item, settings) { let details = `

- ${ item.item_group } | ${ __('Item Code') } : ${ item.item_code } + ${item.item_group} | ${__('Item Code')} : ${item.item_code}

- ${ item.short_description || '' } + ${item.short_description || ''}
- ${ item.formatted_price || '' } + ${item.formatted_price || ''} `; if (item.formatted_mrp) { details += ` - ${ item.formatted_mrp ? item.formatted_mrp.replace(/ +/g, "") : "" } + ${item.formatted_mrp ? item.formatted_mrp.replace(/ +/g, "") : ""} - ${ item.discount } ${ __("OFF") } + ${item.discount} ${__("OFF")} `; } @@ -130,19 +130,19 @@ webshop.ProductList = class { return `
- ${ __("Available on backorder") } + ${__("Available on backorder")} `; } else if (!item.in_stock) { return `
- ${ __("Out of stock") } + ${__("Out of stock")} `; } else if (item.is_stock) { return `
${ __("In stock") } + style="font-size: 14px;">${__("In stock")} `; } } @@ -153,10 +153,10 @@ webshop.ProductList = class { let icon_class = item.wished ? "wished" : "not-wished"; return ` -