Repo comfac-webshop ExtensionPoints: Difference between revisions
"Repo analysis: comfac-hrms and comfac-webshop (14 pages)" |
"Fix markdown code blocks → wikitext syntaxhighlight in Repo analysis pages" |
||
| Line 93: | Line 93: | ||
'''Current:''' Item override in <code>webshop/webshop/doctype/override_doctype/item.py</code> | '''Current:''' Item override in <code>webshop/webshop/doctype/override_doctype/item.py</code> | ||
<syntaxhighlight lang="python"> | |||
from erpnext.stock.doctype.item.item import Item | from erpnext.stock.doctype.item.item import Item | ||
| Line 106: | Line 106: | ||
# Create logic here | # Create logic here | ||
pass | pass | ||
</syntaxhighlight> | |||
'''Registration in hooks.py:''' | '''Registration in hooks.py:''' | ||
<syntaxhighlight lang="python"> | |||
override_doctype_class = { | override_doctype_class = { | ||
"Item": "webshop.webshop.doctype.override_doctype.item.WebshopItem", | "Item": "webshop.webshop.doctype.override_doctype.item.WebshopItem", | ||
} | } | ||
</syntaxhighlight> | |||
=== Pattern 2: Add Custom Cart Validation === | === Pattern 2: Add Custom Cart Validation === | ||
| Line 124: | Line 124: | ||
# Edit <code>webshop/webshop/crud_events/quotation.py</code>: | # Edit <code>webshop/webshop/crud_events/quotation.py</code>: | ||
<syntaxhighlight lang="python"> | |||
def validate_shopping_cart_items(doc, method=None): | def validate_shopping_cart_items(doc, method=None): | ||
"""Existing validation""" | """Existing validation""" | ||
| Line 136: | Line 136: | ||
f"Minimum order amount is {minimum_order} PHP" | f"Minimum order amount is {minimum_order} PHP" | ||
)) | )) | ||
</syntaxhighlight> | |||
# Already registered in hooks.py: | # Already registered in hooks.py: | ||
<syntaxhighlight lang="python"> | |||
doc_events = { | doc_events = { | ||
"Quotation": { | "Quotation": { | ||
| Line 146: | Line 146: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | |||
=== Pattern 3: Custom Product Filter === | === Pattern 3: Custom Product Filter === | ||
| Line 154: | Line 154: | ||
'''Create:''' <code>webshop/webshop/product_data_engine/filters.py</code> | '''Create:''' <code>webshop/webshop/product_data_engine/filters.py</code> | ||
<syntaxhighlight lang="python"> | |||
def apply_new_arrivals_filter(query, filters): | def apply_new_arrivals_filter(query, filters): | ||
"""Show items created in last 30 days""" | """Show items created in last 30 days""" | ||
| Line 164: | Line 164: | ||
) | ) | ||
return query | return query | ||
</syntaxhighlight> | |||
'''Use in template:''' | '''Use in template:''' | ||
| Line 178: | Line 178: | ||
Edit <code>webshop/webshop/doctype/override_doctype/payment_request.py</code>: | Edit <code>webshop/webshop/doctype/override_doctype/payment_request.py</code>: | ||
<syntaxhighlight lang="python"> | |||
from erpnext.accounts.doctype.payment_request.payment_request import PaymentRequest | from erpnext.accounts.doctype.payment_request.payment_request import PaymentRequest | ||
| Line 193: | Line 193: | ||
return self.payment_url | return self.payment_url | ||
return super().get_payment_url() | return super().get_payment_url() | ||
</syntaxhighlight> | |||
== Adding New Web Pages == | == Adding New Web Pages == | ||
| Line 201: | Line 201: | ||
'''Step 1: Create controller''' <code>webshop/www/track-order/index.py</code>: | '''Step 1: Create controller''' <code>webshop/www/track-order/index.py</code>: | ||
<syntaxhighlight lang="python"> | |||
import frappe | import frappe | ||
from frappe import _ | from frappe import _ | ||
| Line 218: | Line 218: | ||
# Custom tracking logic | # Custom tracking logic | ||
pass | pass | ||
</syntaxhighlight> | |||
'''Step 2: Create template''' <code>webshop/www/track-order/index.html</code>: | '''Step 2: Create template''' <code>webshop/www/track-order/index.html</code>: | ||
<syntaxhighlight lang="html"> | |||
{% extends "templates/web_base.html" %} | {% extends "templates/web_base.html" %} | ||
| Line 242: | Line 242: | ||
</div> | </div> | ||
{% endblock %} | {% endblock %} | ||
</syntaxhighlight> | |||
'''Step 3: Access at''' <code>/track-order</code> | '''Step 3: Access at''' <code>/track-order</code> | ||
| Line 262: | Line 262: | ||
In <code>webshop/hooks.py</code>: | In <code>webshop/hooks.py</code>: | ||
<syntaxhighlight lang="python"> | |||
fixtures = [ | fixtures = [ | ||
{ | { | ||
| Line 269: | Line 269: | ||
} | } | ||
] | ] | ||
</syntaxhighlight> | |||
Then run: <code>bench export-fixtures</code> | Then run: <code>bench export-fixtures</code> | ||
| Line 281: | Line 281: | ||
Copy original from Frappe and modify: | Copy original from Frappe and modify: | ||
<syntaxhighlight lang="html"> | |||
<!-- Custom item card with ComFac branding --> | <!-- Custom item card with ComFac branding --> | ||
<div class="card product-card"> | <div class="card product-card"> | ||
| Line 297: | Line 297: | ||
</a> | </a> | ||
</div> | </div> | ||
</syntaxhighlight> | |||
== JavaScript Customization == | == JavaScript Customization == | ||
| Line 305: | Line 305: | ||
Edit <code>webshop/public/js/cart.js</code>: | Edit <code>webshop/public/js/cart.js</code>: | ||
<syntaxhighlight lang="javascript"> | |||
// Add to existing file or create new | // Add to existing file or create new | ||
frappe.ready(function() { | frappe.ready(function() { | ||
| Line 319: | Line 319: | ||
}); | }); | ||
}); | }); | ||
</syntaxhighlight> | |||
Register in <code>webshop/hooks.py</code>: | Register in <code>webshop/hooks.py</code>: | ||
<syntaxhighlight lang="python"> | |||
web_include_js = [ | web_include_js = [ | ||
"web.bundle.js", | "web.bundle.js", | ||
"webshop/js/cart.js" # Add custom file | "webshop/js/cart.js" # Add custom file | ||
] | ] | ||
</syntaxhighlight> | |||
== Related Pages == | == Related Pages == | ||
Latest revision as of 16:40, 9 March 2026
Repo:comfac-webshop/ExtensionPoints — Extension and Customization Points
The Safe Edit Zone
Frappe Override Pattern
Like comfac-hrms, webshop uses the "override instead of modify" pattern:
| Location | What to Put There |
|---|---|
webshop/webshop/doctype/override_doctype/ |
Override ERPNext classes |
webshop/webshop/crud_events/ |
Event handlers for sync logic |
webshop/public/js/ |
Custom client-side JavaScript |
webshop/templates/ |
Custom Jinja templates |
webshop/www/ |
New web pages or route overrides |
| Custom Fields (via UI) | Non-core fields |
Files You Should NOT Edit Directly
- ERPNext core (Item, Quotation, etc.)
- Frappe framework
- Third-party libraries
Hooks & Events
Current Hooks in webshop/hooks.py
| Hook Type | Purpose | Current Usage |
|---|---|---|
after_install |
Post-install setup | Create default Webshop Settings |
on_logout |
Cleanup | Clear cart count cookie |
on_session_creation |
Initialize | Update debtors account, set cart count |
update_website_context |
Template data | Add cart info to all pages |
website_generators |
Auto-routes | Website Item, Item Group pages |
override_doctype_class |
Class overrides | Item, Item Group, Payment Request |
doctype_js |
Client scripts | Item, Homepage forms |
doc_events |
Server events | Item sync, Quotation validation |
has_website_permission |
Access control | Website Item, Item Group visibility |
Document Event Hooks
| DocType | Event | Handler | Purpose |
|---|---|---|---|
| Item | on_update | crud_events.item.update_website_item |
Sync to Website Item |
| Item | on_update | crud_events.item.invalidate_item_variants_cache |
Clear cache |
| Item | before_rename | crud_events.item.validate_duplicate_website_item |
Prevent conflicts |
| Item | after_rename | crud_events.item.invalidate_item_variants_cache |
Clear cache |
| Sales Taxes Template | on_update | webshop_settings.validate_cart_settings |
Validate config |
| Quotation | validate | crud_events.quotation.validate_shopping_cart_items |
Cart validation |
| Price List | validate | crud_events.price_list.check_impact_on_cart |
Warn users |
| Tax Rule | validate | crud_events.tax_rule.validate_use_for_cart |
Compatibility check |
Session Hooks
| Hook | When Fired | Handler |
|---|---|---|
on_session_creation |
User logs in | Update debtors account, set cart count |
on_logout |
User logs out | Clear cart count cookie |
Override Patterns
Pattern 1: Override ERPNext DocType
Current: Item override in webshop/webshop/doctype/override_doctype/item.py
from erpnext.stock.doctype.item.item import Item
class WebshopItem(Item):
def on_update(self):
super().on_update() # Call parent
# Custom: Auto-create Website Item
self.create_website_item()
def create_website_item(self):
if not frappe.db.exists("Website Item", {"item_code": self.name}):
# Create logic here
pass
Registration in hooks.py:
override_doctype_class = {
"Item": "webshop.webshop.doctype.override_doctype.item.WebshopItem",
}
Pattern 2: Add Custom Cart Validation
Goal: Minimum order amount check
Steps:
- Edit
webshop/webshop/crud_events/quotation.py:
def validate_shopping_cart_items(doc, method=None):
"""Existing validation"""
# ... existing code ...
# Add custom validation
if doc.order_type == "Shopping Cart":
minimum_order = 1000 # PHP
if doc.total < minimum_order:
frappe.throw(_(
f"Minimum order amount is {minimum_order} PHP"
))
- Already registered in hooks.py:
doc_events = {
"Quotation": {
"validate": ["webshop.webshop.crud_events.quotation.validate_shopping_cart_items"]
}
}
Pattern 3: Custom Product Filter
Goal: Add "New Arrivals" filter
Create: webshop/webshop/product_data_engine/filters.py
def apply_new_arrivals_filter(query, filters):
"""Show items created in last 30 days"""
from datetime import datetime, timedelta
thirty_days_ago = datetime.now() - timedelta(days=30)
query = query.where(
frappe.qb.DocType("Website Item").creation >= thirty_days_ago
)
return query
Use in template:
Add to product listing template to call custom filter.
Pattern 4: Custom Payment Gateway Integration
Goal: Add Philippines-specific payment method (e.g., GCash)
Override Payment Request:
Edit webshop/webshop/doctype/override_doctype/payment_request.py:
from erpnext.accounts.doctype.payment_request.payment_request import PaymentRequest
class WebshopPaymentRequest(PaymentRequest):
def on_submit(self):
super().on_submit()
if self.payment_gateway == "GCash":
# Generate GCash payment URL
self.payment_url = generate_gcash_url(self)
def get_payment_url(self):
if self.payment_gateway == "GCash":
return self.payment_url
return super().get_payment_url()
Adding New Web Pages
Create a New Page: Order Tracking
Step 1: Create controller webshop/www/track-order/index.py:
import frappe
from frappe import _
def get_context(context):
context.no_cache = 1
order_id = frappe.form_dict.get('order')
if order_id:
context.order = frappe.get_doc("Sales Order", order_id)
context.tracking_info = get_tracking_info(order_id)
return context
def get_tracking_info(order_id):
# Custom tracking logic
pass
Step 2: Create template webshop/www/track-order/index.html:
{% extends "templates/web_base.html" %}
{% block page_content %}
<div class="container">
<h1>Track Your Order</h1>
{% if order %}
<div class="order-details">
<h3>Order #{{ order.name }}</h3>
<p>Status: {{ order.status }}</p>
<!-- Tracking display -->
</div>
{% else %}
<form method="GET">
<input type="text" name="order" placeholder="Enter Order Number">
<button type="submit">Track</button>
</form>
{% endif %}
</div>
{% endblock %}
Step 3: Access at /track-order
Custom Fields Best Practice
| Approach | When to Use | How |
|---|---|---|
| Custom Field (UI) | Quick additions | Desk → Customize Form |
| Fixtures | Version-controlled fields | Add to hooks.py fixtures |
| DocType JSON edit | Core app changes | Edit .json, migrate |
Export Custom Fields as Fixtures
In webshop/hooks.py:
fixtures = [
{
"dt": "Custom Field",
"filters": [["module", "=", "Webshop"]]
}
]
Then run: bench export-fixtures
Template Overrides
Override Product Card
Create: webshop/templates/webshop/item_card.html
Copy original from Frappe and modify:
<!-- Custom item card with ComFac branding -->
<div class="card product-card">
<a href="{{ item.route }}">
<img src="{{ item.website_image or '/assets/webshop/images/fallback.png' }}"
alt="{{ item.web_item_name }}">
<div class="card-body">
<h5>{{ item.web_item_name }}</h5>
<p class="price">₱{{ item.price }}</p>
<!-- Custom badge -->
{% if item.on_backorder %}
<span class="badge badge-warning">Backorder</span>
{% endif %}
</div>
</a>
</div>
JavaScript Customization
Add Custom Cart Behavior
Edit webshop/public/js/cart.js:
// Add to existing file or create new
frappe.ready(function() {
// Custom: Show confirmation modal for expensive items
$('.add-to-cart').on('click', function(e) {
var price = $(this).data('price');
if (price > 50000) { // PHP 50,000
if (!confirm('This item costs ₱' + price + '. Add to cart?')) {
e.preventDefault();
return false;
}
}
});
});
Register in webshop/hooks.py:
web_include_js = [
"web.bundle.js",
"webshop/js/cart.js" # Add custom file
]
Related Pages
- Repo_comfac-webshop_Overview — Project overview
- Repo_comfac-webshop_DirectoryMap — Directory structure and key files
- Repo_comfac-webshop_EntryPoints — How the app starts
- Repo_comfac-webshop_DataModel — DocTypes and data model
- Repo_comfac-webshop_CommonEdits — Common edit recipes
- Repo_comfac-hrms_ExtensionPoints — Extension points for comfac-hrms