Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0606d2f
feat(shopify): implement multi-tenant architecture for Shopify integr…
engahmed1190 Aug 13, 2025
4c668c3
refactor(shopify): standardize account resolution across modules
engahmed1190 Aug 13, 2025
05a0562
feat(shopify): improve account authentication and sync logic
engahmed1190 Aug 13, 2025
76e32ed
refactor(shopify): extract location fetching logic to separate method
engahmed1190 Aug 13, 2025
86cfa5e
refactor(shopify): standardize account parameter in logging functions
engahmed1190 Aug 13, 2025
0425202
refactor(shopify_account): Remove redundant shop_domain field and add…
engahmed1190 Aug 13, 2025
5a3e7a9
feat(shopify): add customer group field and refactor form handlers
engahmed1190 Aug 13, 2025
2e3a656
feat(shopify): add default tax and shipping accounts and form improve…
engahmed1190 Aug 13, 2025
ddd632a
feat(shopify): add account parameter to product sync methods
engahmed1190 Aug 14, 2025
ec2f944
[CS-61 Fix the logic by adding setting.is_enabled() condition]
goondeal Sep 21, 2025
8bc637e
Merge pull request #1 from BrainWise-DEV/CS-61-fix-a-bug
mahmoudhaney Sep 22, 2025
21fa1c3
sync shopify locations with erpnext warehouses
goondeal Oct 21, 2025
40bcc1c
Complete syncing product creation on when create erpnext item
goondeal Oct 21, 2025
ae56cdb
Refactor Shopify Integration: Transition to Account-Based Configuration
goondeal Oct 22, 2025
56e660b
Remove Shopify Setting integration files and related test cases
goondeal Oct 22, 2025
ee21979
add company field to Ecommerce item to separate for multi-tenant
goondeal Oct 28, 2025
1635fba
Enhance Shopify integration:
goondeal Oct 28, 2025
c06b196
fix products sync functionality
goondeal Nov 17, 2025
7b7182c
Merge pull request #2 from BrainWise-DEV/CS-86-shopify-support-multi-…
mahmoudhaney Nov 23, 2025
b17ec1c
set shopify product price as a shopify_selling_rate in the newly crea…
goondeal Dec 9, 2025
eeb6fa6
Merge pull request #3 from BrainWise-DEV/CS-156-sync-shopify-products…
goondeal Dec 9, 2025
0b168b3
Enhance retry functionality and Shopify account handling in Ecommerce…
goondeal Dec 10, 2025
0c67bc9
Merge pull request #4 from BrainWise-DEV/CS-156-sync-shopify-products…
goondeal Dec 10, 2025
98ca788
create get_shopify_locations method for shopify account
goondeal Dec 10, 2025
fbc2a7e
fixing issue with create item to check first if company account is ex…
MohamedAliSmk Feb 8, 2026
0d94675
Merge pull request #6 from BrainWise-DEV/fix-cerate_item-issue
goondeal Feb 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion ecommerce_integrations/controllers/customer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def get_customer_doc(self):
else:
raise frappe.DoesNotExistError()

def sync_customer(self, customer_name: str, customer_group: str) -> None:
def sync_customer(self, customer_name: str, customer_group: str, **kwargs) -> None:
"""Create customer in ERPNext if one does not exist already."""
customer = frappe.get_doc(
{
Expand All @@ -35,6 +35,9 @@ def sync_customer(self, customer_name: str, customer_group: str) -> None:
}
)

if kwargs.get("company"):
customer.custom_company = kwargs["company"]

customer.flags.ignore_mandatory = True
customer.insert(ignore_permissions=True)

Expand Down
9 changes: 5 additions & 4 deletions ecommerce_integrations/controllers/scheduling.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from frappe.utils import add_to_date, cint, get_datetime, now


def need_to_run(setting, interval_field, timestamp_field) -> bool:
# TODO: handle unicommerce
def need_to_run(setting, doc_name, interval_field, timestamp_field) -> bool:
"""A utility function to make "configurable" scheduled events.

If timestamp_field is older than current_time - inveterval_field then this function updates the timestamp_field to `now()` and returns True,
Expand All @@ -13,11 +14,11 @@ def need_to_run(setting, interval_field, timestamp_field) -> bool:
- timestamp field is datetime field.
- This function is called from scheuled job with less frequency than lowest interval_field. Ideally, every minute.
"""
interval = frappe.db.get_single_value(setting, interval_field, cache=True)
last_run = frappe.db.get_single_value(setting, timestamp_field)
interval = frappe.db.get_value(setting, doc_name, interval_field, cache=True)
last_run = frappe.db.get_value(setting, doc_name, timestamp_field)

if last_run and get_datetime() < get_datetime(add_to_date(last_run, minutes=cint(interval, default=10))):
return False

frappe.db.set_value(setting, None, timestamp_field, now(), update_modified=False)
frappe.db.set_value(setting, doc_name, timestamp_field, now(), update_modified=False)
return True
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@

frappe.ui.form.on("Ecommerce Integration Log", {
refresh: function (frm) {
if (frm.doc.request_data && frm.doc.status == "Error") {
const retryStatusList = ["Error", "Invalid"]
if (frm.doc.request_data && retryStatusList.includes(frm.doc.status)) {
frm.add_custom_button(__("Retry"), function () {
frappe.call({
method: "ecommerce_integrations.ecommerce_integrations.doctype.ecommerce_integration_log.ecommerce_integration_log.resync",
args: {
method: frm.doc.method,
name: frm.doc.name,
request_data: frm.doc.request_data,
shopify_account: frm.doc.shopify_account,
},
callback: function (r) {
frappe.msgprint(__("Reattempting to sync"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"field_order": [
"title",
"integration",
"shopify_account",
"status",
"method",
"message",
Expand Down Expand Up @@ -66,12 +67,21 @@
"fieldtype": "Code",
"label": "Response Data",
"read_only": 1
},
{
"depends_on": "eval: doc.integration == \"shopify\"",
"fieldname": "shopify_account",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Shopify Account",
"options": "Shopify Account"
}
],
"in_create": 1,
"links": [],
"modified": "2021-05-28 16:06:49.008875",
"modified_by": "Administrator",
"modified": "2025-10-28 16:06:18.084320",
"modified_by": "user@nexusdemo.com",
"module": "Ecommerce Integrations",
"name": "Ecommerce Integration Log",
"owner": "Administrator",
Expand Down Expand Up @@ -101,7 +111,9 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "title"
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@


class EcommerceIntegrationLog(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from frappe.types import DF

integration: DF.Link | None
message: DF.Code | None
method: DF.SmallText | None
request_data: DF.Code | None
response_data: DF.Code | None
shopify_account: DF.Link | None
status: DF.Data | None
title: DF.Data | None
traceback: DF.Code | None
# end: auto-generated types
def validate(self):
self._set_title()

Expand Down Expand Up @@ -47,6 +65,7 @@ def create_log(
method=None,
message=None,
make_new=False,
shopify_account=None,
):
make_new = make_new or not bool(frappe.flags.request_id)

Expand All @@ -71,6 +90,7 @@ def create_log(
log.request_data = request_data or log.request_data
log.traceback = log.traceback or frappe.get_traceback()
log.status = status
log.shopify_account = shopify_account
log.save(ignore_permissions=True)

frappe.db.commit()
Expand All @@ -96,19 +116,22 @@ def _retry_job(job: str):
frappe.only_for("System Manager")

doc = frappe.get_doc("Ecommerce Integration Log", job)
if not doc.method.startswith("ecommerce_integrations.") or doc.status != "Error":
retry_status_list = ["Error", "Invalid"]
if not doc.method.startswith("ecommerce_integrations.") or doc.status not in retry_status_list:
return

doc.db_set("status", "Queued", update_modified=False)
doc.db_set("traceback", "", update_modified=False)

shopify_account = frappe.get_doc("Shopify Account", doc.shopify_account) if doc.shopify_account else None

frappe.enqueue(
method=doc.method,
queue="short",
timeout=300,
is_async=True,
payload=json.loads(doc.request_data),
request_id=doc.name,
shopify_account=shopify_account,
enqueue_after_commit=True,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"erpnext_item_code",
"integration_item_code",
"sku",
"company",
"column_break_5",
"has_variants",
"variant_id",
Expand Down Expand Up @@ -91,12 +92,20 @@
"fieldtype": "Datetime",
"label": "Item Data Synced On",
"read_only": 1
},
{
"fetch_from": "erpnext_item_code.custom_company",
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2022-11-15 11:36:04.733227",
"modified_by": "Administrator",
"modified": "2025-10-26 11:38:45.690155",
"modified_by": "user@nexus.com",
"module": "Ecommerce Integrations",
"name": "Ecommerce Item",
"owner": "Administrator",
Expand All @@ -114,6 +123,7 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ def create_ecommerce_item(
"doctype": "Item",
"is_stock_item": 1,
"is_sales_item": 1,
"item_defaults": [{"company": get_default_company()}],
"item_defaults": [
{
"company": item_dict.get("custom_company"),
"default_warehouse": item_dict.get("default_warehouse"),
}
],
}

item.update(item_dict)
Expand Down
35 changes: 22 additions & 13 deletions ecommerce_integrations/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,14 @@
# ---------------

scheduler_events = {
"all": ["ecommerce_integrations.shopify.inventory.update_inventory_on_shopify"],
"all": [
# Updated to use multi-tenant inventory sync
"ecommerce_integrations.shopify.inventory.update_inventory_on_shopify"
],
"daily": [],
"daily_long": ["ecommerce_integrations.zenoti.doctype.zenoti_settings.zenoti_settings.sync_stocks"],
"hourly": [
# Updated to use multi-tenant old orders sync
"ecommerce_integrations.shopify.order.sync_old_orders",
"ecommerce_integrations.amazon.doctype.amazon_sp_api_settings.amazon_sp_api_settings.schedule_get_order_details",
],
Expand Down Expand Up @@ -198,21 +202,26 @@
# "filter_by": "{filter_by}",
# "redact_fields": ["{field_1}", "{field_2}"],
# "partial": 1,
# },
# {
# "doctype": "{doctype_2}",
# "filter_by": "{filter_by}",
# "partial": 1,
# },
# {
# "doctype": "{doctype_3}",
# "strict": False,
# },
# {
# "doctype": "{doctype_4}"
# }
# ]

# Authentication and authorization
# --------------------------------

# auth_hooks = [
# "ecommerce_integrations.auth.validate"
# ]

# Translation
# --------------------------------

# Make property setters available in the translation file
# translate_linked_doctypes = ["DocType", "Role"]

# Automatically update python controller files with type annotations for DocTypes
# Automatically add __init__.py files to the DocTypes
# be careful turning this on if you have customizations.
export_python_type_annotations = True

default_log_clearing_doctypes = {
"Ecommerce Integration Log": 120,
Expand Down
Loading