diff --git a/custom_ui/api/db/estimates.py b/custom_ui/api/db/estimates.py
index 8aadf5a..678200b 100644
--- a/custom_ui/api/db/estimates.py
+++ b/custom_ui/api/db/estimates.py
@@ -4,7 +4,7 @@ from custom_ui.api.db.general import get_doc_history
from custom_ui.db_utils import DbUtils, process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
from werkzeug.wrappers import Response
from custom_ui.api.db.clients import check_if_customer, convert_lead_to_customer
-from custom_ui.services import DbService, ClientService, AddressService, ContactService, EstimateService, ItemService
+from custom_ui.services import DbService, ClientService, AddressService, ContactService
# ===============================================================================
# ESTIMATES & INVOICES API METHODS
@@ -86,25 +86,11 @@ def get_estimate_table_data(filters={}, sortings=[], page=1, page_size=10):
@frappe.whitelist()
-def get_quotation_items(project_template:str = None):
+def get_quotation_items():
"""Get all available quotation items."""
try:
- filters = EstimateService.map_project_template_to_filter(project_template)
- items = frappe.get_all("Item", fields=["item_code", "item_group"], filters=filters)
- grouped_item_dicts = {}
- for item in items:
- item_dict = ItemService.get_full_dict(item.item_code)
- if item_dict["bom"]:
- if "Packages" not in grouped_item_dicts:
- grouped_item_dicts["Packages"] = {}
- if item.item_group not in grouped_item_dicts["Packages"]:
- grouped_item_dicts["Packages"][item.item_group] = []
- grouped_item_dicts["Packages"][item.item_group].append(item_dict)
- else:
- if item.item_group not in grouped_item_dicts:
- grouped_item_dicts[item.item_group] = []
- grouped_item_dicts[item.item_group].append(item_dict)
- return build_success_response(grouped_item_dicts)
+ items = frappe.get_all("Item", fields=["*"], filters={"item_group": "SNW-S"})
+ return build_success_response(items)
except Exception as e:
return build_error_response(str(e), 500)
diff --git a/custom_ui/api/public/payments.py b/custom_ui/api/public/payments.py
index 9f4a88a..b369087 100644
--- a/custom_ui/api/public/payments.py
+++ b/custom_ui/api/public/payments.py
@@ -1,8 +1,7 @@
import frappe
import json
from frappe.utils.data import flt
-from custom_ui.services import DbService, StripeService, PaymentService
-from custom_ui.models import PaymentData
+from custom_ui.services import DbService, StripeService
@frappe.whitelist(allow_guest=True)
def half_down_stripe_payment(sales_order):
@@ -32,36 +31,37 @@ def stripe_webhook():
sig_header = frappe.request.headers.get('Stripe-Signature')
session, metadata = StripeService.get_session_and_metadata(payload, sig_header)
- required_keys = ["order_num", "company", "payment_type"]
- for key in required_keys:
- if not metadata.get(key):
- raise frappe.ValidationError(f"Missing required metadata key: {key}")
-
if DbService.exists("Payment Entry", {"reference_no": session.id}):
raise frappe.ValidationError("Payment Entry already exists for this session.")
-
-
reference_doctype = "Sales Invoice"
+
if metadata.get("payment_type") == "advance":
reference_doctype = "Sales Order"
elif metadata.get("payment_type") != "full":
raise frappe.ValidationError("Invalid payment type in metadata.")
amount_paid = flt(session.amount_total) / 100
+ currency = session.currency.upper()
+ reference_doc = frappe.get_doc(reference_doctype, metadata.get("order_num"))
- # stripe_settings = StripeService.get_stripe_settings(metadata.get("company"))
+ pe = frappe.get_doc({
+ "doctype": "Payment Entry",
+ "payment_type": "Receive",
+ "party_type": "Customer",
+ "mode_of_payment": "Stripe",
+ "party": reference_doc.customer,
+ "party_name": reference_doc.customer,
+ "paid_to": metadata.get("company"),
+ "reference_no": session.id,
+ "reference_date": frappe.utils.nowdate(),
+ "reference_doctype": reference_doctype,
+ "reference_name": reference_doc.name,
+ "paid_amount": amount_paid,
+ "paid_currency": currency,
+ })
- pe = PaymentService.create_payment_entry(
- data=PaymentData(
- mode_of_payment="Stripe",
- reference_no=session.id,
- reference_date=session.created,
- received_amount=amount_paid,
- company=metadata.get("company"),
- reference_doc_name=metadata.get("order_num")
- )
- )
+ pe.insert()
pe.submit()
return "Payment Entry created and submitted successfully."
diff --git a/custom_ui/install.py b/custom_ui/install.py
index 450e82d..c4a42ae 100644
--- a/custom_ui/install.py
+++ b/custom_ui/install.py
@@ -24,8 +24,6 @@ def after_install():
create_task_types()
# create_tasks()
create_bid_meeting_note_form_templates()
- create_accounts()
- init_stripe_accounts()
build_frontend()
def after_migrate():
@@ -44,8 +42,6 @@ def after_migrate():
create_task_types()
# create_tasks()
create_bid_meeting_note_form_templates()
- create_accounts()
- init_stripe_accounts()
# update_address_fields()
# build_frontend()
@@ -1382,58 +1378,3 @@ def create_bid_meeting_note_form_templates():
)
doc.insert(ignore_permissions=True)
-
-def create_accounts():
- """Create necessary accounts if they do not exist."""
- print("\nš§ Checking for necessary accounts...")
-
- accounts = [
- {
- "Sprinklers Northwest": [
- {
- "account_name": "Stripe Clearing - Sprinklers Northwest",
- "account_type": "Bank",
- "parent_account": "Bank Accounts - S",
- "company": "Sprinklers Northwest"
- }
- ]
- }
- ]
-
- for company_accounts in accounts:
- for company, account_list in company_accounts.items():
- for account in account_list:
- # Idempotency check
- if frappe.db.exists("Account", {"account_name": account["account_name"], "company": account["company"]}):
- continue
- doc = frappe.get_doc({
- "doctype": "Account",
- "account_name": account["account_name"],
- "account_type": account["account_type"],
- "company": account["company"],
- "parent_account": account["parent_account"],
- "is_group": 0
- })
- doc.insert(ignore_permissions=True)
-
- frappe.db.commit()
-
-def init_stripe_accounts():
- """Initializes the bare configurations for each Stripe Settings doctypes."""
- print("\nš§ Initializing Stripe Settings for companies...")
-
- companies = ["Sprinklers Northwest"]
-
- for company in companies:
- if not frappe.db.exists("Stripe Settings", {"company": company}):
- doc = frappe.get_doc({
- "doctype": "Stripe Settings",
- "company": company,
- "api_key": "",
- "publishable_key": "",
- "webhook_secret": "",
- "account": f"Stripe Clearing - {company}"
- })
- doc.insert(ignore_permissions=True)
-
- frappe.db.commit()
diff --git a/custom_ui/models/__init__.py b/custom_ui/models/__init__.py
deleted file mode 100644
index c0134af..0000000
--- a/custom_ui/models/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .payments import PaymentData
\ No newline at end of file
diff --git a/custom_ui/models/payments.py b/custom_ui/models/payments.py
deleted file mode 100644
index dfd9bf4..0000000
--- a/custom_ui/models/payments.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from dataclasses import dataclass
-
-@dataclass
-class PaymentData:
- mode_of_payment: str
- reference_no: str
- reference_date: str
- received_amount: float
- company: str = None
- reference_doc_name: str = None
\ No newline at end of file
diff --git a/custom_ui/services/__init__.py b/custom_ui/services/__init__.py
index d5fafc8..a1d4631 100644
--- a/custom_ui/services/__init__.py
+++ b/custom_ui/services/__init__.py
@@ -6,6 +6,4 @@ from .estimate_service import EstimateService
from .onsite_meeting_service import OnSiteMeetingService
from .task_service import TaskService
from .service_appointment_service import ServiceAppointmentService
-from .stripe_service import StripeService
-from .payment_service import PaymentService
-from .item_service import ItemService
\ No newline at end of file
+from .stripe_service import StripeService
\ No newline at end of file
diff --git a/custom_ui/services/estimate_service.py b/custom_ui/services/estimate_service.py
index ea05a8a..d5e3cf2 100644
--- a/custom_ui/services/estimate_service.py
+++ b/custom_ui/services/estimate_service.py
@@ -1,5 +1,4 @@
import frappe
-from .item_service import ItemService
class EstimateService:
@@ -94,18 +93,4 @@ class EstimateService:
estimate_doc.customer = customer_name
estimate_doc.save(ignore_permissions=True)
print(f"DEBUG: Linked Quotation {estimate_doc.name} to {customer_type} {customer_name}")
-
- @staticmethod
- def map_project_template_to_filter(project_template: str = None) -> dict | None:
- """Map a project template to a filter."""
- print(f"DEBUG: Mapping project template {project_template} to quotation category")
- if not project_template:
- print("DEBUG: No project template provided, defaulting to 'General'")
- return None
- mapping = {
- # SNW Install is both Irrigation and SNW-S categories
- "SNW Install": ["in", ["Irrigation", "SNW-S", "Landscaping"]],
- }
- category = mapping.get(project_template, "General")
- print(f"DEBUG: Mapped to quotation category: {category}")
- return { "item_group": category }
\ No newline at end of file
+
\ No newline at end of file
diff --git a/custom_ui/services/item_service.py b/custom_ui/services/item_service.py
deleted file mode 100644
index 4e4e3de..0000000
--- a/custom_ui/services/item_service.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import frappe
-
-class ItemService:
-
- @staticmethod
- def get_item_category(item_code: str) -> str:
- """Retrieve the category of an Item document by item code."""
- print(f"DEBUG: Getting category for Item {item_code}")
- category = frappe.db.get_value("Item", item_code, "item_group")
- print(f"DEBUG: Retrieved category: {category}")
- return category
-
- @staticmethod
- def get_full_dict(item_code: str) -> frappe._dict:
- """Retrieve the full Item document by item code."""
- print(f"DEBUG: Getting full document for Item {item_code}")
- item_doc = frappe.get_doc("Item", item_code).as_dict()
- item_doc["bom"] = ItemService.get_full_bom_dict(item_code) if item_doc.get("default_bom") else None
- return item_doc
-
- @staticmethod
- def get_full_bom_dict(item_code: str):
- """Retrieve the Bill of Materials (BOM) associated with an Item."""
- print(f"DEBUG: Getting BOM for Item {item_code}")
- bom_name = frappe.db.get_value("BOM", {"item": item_code, "is_active": 1}, "name")
- bom_dict = frappe.get_doc("BOM", bom_name).as_dict()
- for item in bom_dict.get('exploded_items', []):
- item_bom_name = frappe.get_value("Item", item["item_name"], "default_bom")
- item["bom"] = frappe.get_doc("BOM", item_bom_name).as_dict() if item_bom_name else None
- return bom_dict
-
- @staticmethod
- def exists(item_code: str) -> bool:
- """Check if an Item document exists by item code."""
- print(f"DEBUG: Checking existence of Item {item_code}")
- exists = frappe.db.exists("Item", item_code) is not None
- print(f"DEBUG: Item {item_code} exists: {exists}")
- return exists
\ No newline at end of file
diff --git a/custom_ui/services/payment_service.py b/custom_ui/services/payment_service.py
index db84f18..ca447f1 100644
--- a/custom_ui/services/payment_service.py
+++ b/custom_ui/services/payment_service.py
@@ -1,51 +1,29 @@
import frappe
-from custom_ui.services import DbService, StripeService
-from dataclasses import dataclass
-from custom_ui.models import PaymentData
-
-
+from custom_ui.services import DbService
class PaymentService:
@staticmethod
- def create_payment_entry(data: PaymentData) -> frappe._dict:
+ def create_payment_entry(reference_doctype: str, reference_doc_name: str, data: dict) -> frappe._dict:
"""Create a Payment Entry document based on the reference document."""
- print(f"DEBUG: Creating Payment Entry for {data.reference_doc_name} with data: {data}")
- reference_doctype = PaymentService.determine_reference_doctype(data.reference_doc_name)
- reference_doc = DbService.get_or_throw(reference_doctype, data.reference_doc_name)
- account = StripeService.get_stripe_settings(data.company).account
+ print(f"DEBUG: Creating Payment Entry for {reference_doctype} {reference_doc_name} with data: {data}")
+ reference_doc = DbService.get_or_throw(reference_doctype, reference_doc_name)
pe = frappe.get_doc({
"doctype": "Payment Entry",
- "company": data.company,
"payment_type": "Receive",
"party_type": "Customer",
- "mode_of_payment": data.mode_of_payment or "Stripe",
+ "mode_of_payment": data.get("mode_of_payment", "Stripe"),
"party": reference_doc.customer,
"party_name": reference_doc.customer,
- "paid_to": account,
- "reference_no": data.reference_no,
- "reference_date": data.reference_date or frappe.utils.nowdate(),
- "received_amount": data.received_amount,
- "paid_currency": "USD",
- "received_currency": "USD",
- }).append("references", {
- "reference_doctype": reference_doc.doctype,
+ "paid_to": data.get("paid_to"),
+ "reference_no": data.get("reference_no"),
+ "reference_date": data.get("reference_date", frappe.utils.nowdate()),
+ "reference_doctype": reference_doctype,
"reference_name": reference_doc.name,
- "reconcile_effect_on": reference_doc.doctype,
- "allocated_amount": data.received_amount,
+ "paid_amount": data.get("paid_amount"),
+ "paid_currency": data.get("paid_currency"),
})
pe.insert()
print(f"DEBUG: Created Payment Entry with name: {pe.name}")
- return pe
-
- @staticmethod
- def determine_reference_doctype(reference_doc_name: str) -> str:
- """Determine the reference doctype based on the document name pattern."""
- print(f"DEBUG: Determining reference doctype for document name: {reference_doc_name}")
- if DbService.exists("Sales Order", reference_doc_name):
- return "Sales Order"
- elif DbService.exists("Sales Invoice", reference_doc_name):
- return "Sales Invoice"
- else:
- frappe.throw("Unable to determine reference doctype from document name.")
+ return pe.as_dict()
\ No newline at end of file
diff --git a/custom_ui/services/sales_order_service.py b/custom_ui/services/sales_order_service.py
index 0bdeca3..9730618 100644
--- a/custom_ui/services/sales_order_service.py
+++ b/custom_ui/services/sales_order_service.py
@@ -1,2 +1,7 @@
+import frappe
-
\ No newline at end of file
+class SalesOrderService:
+
+ @staticmethod
+ def apply_advance_payment(sales_order_name: str, payment_entry_doc):
+ pass
\ No newline at end of file
diff --git a/docker-compose.local.yaml b/docker-compose.local.yaml
deleted file mode 100644
index b99b3de..0000000
--- a/docker-compose.local.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-services:
- mailhog:
- image: mailhog/mailhog:latest
- container_name: mailhog
- ports:
- - "8025:8025" # MailHog web UI
- - "1025:1025" # SMTP server
- restart: unless-stopped
\ No newline at end of file
diff --git a/frontend/src/api.js b/frontend/src/api.js
index a1da164..01e1baf 100644
--- a/frontend/src/api.js
+++ b/frontend/src/api.js
@@ -229,8 +229,8 @@ class Api {
// ESTIMATE / QUOTATION METHODS
// ============================================================================
- static async getQuotationItems(projectTemplate) {
- return await this.request("custom_ui.api.db.estimates.get_quotation_items", { projectTemplate });
+ static async getQuotationItems() {
+ return await this.request("custom_ui.api.db.estimates.get_quotation_items");
}
static async getEstimateFromAddress(fullAddress) {
diff --git a/frontend/src/components/common/ItemSelector.vue b/frontend/src/components/common/ItemSelector.vue
deleted file mode 100644
index 46ee774..0000000
--- a/frontend/src/components/common/ItemSelector.vue
+++ /dev/null
@@ -1,145 +0,0 @@
-
-