update bugs
This commit is contained in:
parent
520e239741
commit
fe46f18d60
@ -253,17 +253,25 @@ def upsert_client(data):
|
|||||||
print("#####DEBUG: Processing contact data:", contact_data)
|
print("#####DEBUG: Processing contact data:", contact_data)
|
||||||
contact_exists = frappe.db.exists("Contact", {"email_id": contact_data.get("email"), "phone": contact_data.get("phone_number")})
|
contact_exists = frappe.db.exists("Contact", {"email_id": contact_data.get("email"), "phone": contact_data.get("phone_number")})
|
||||||
if not contact_exists:
|
if not contact_exists:
|
||||||
is_primary_contact = 1 if contact_data.get("is_primary_contact") else 0
|
|
||||||
contact_doc = frappe.get_doc({
|
contact_doc = frappe.get_doc({
|
||||||
"doctype": "Contact",
|
"doctype": "Contact",
|
||||||
"first_name": contact_data.get("first_name"),
|
"first_name": contact_data.get("first_name"),
|
||||||
"last_name": contact_data.get("last_name"),
|
"last_name": contact_data.get("last_name"),
|
||||||
"email_id": contact_data.get("email"),
|
# "email_id": contact_data.get("email"),
|
||||||
"phone": contact_data.get("phone_number"),
|
# "phone": contact_data.get("phone_number"),
|
||||||
"custom_customer": customer_doc.name,
|
"custom_customer": customer_doc.name,
|
||||||
"role": contact_data.get("contact_role", "Other"),
|
"role": contact_data.get("contact_role", "Other"),
|
||||||
"custom_email": contact_data.get("email"),
|
"custom_email": contact_data.get("email"),
|
||||||
"is_primary_contact": is_primary_contact
|
"is_primary_contact": data.get("is_primary", False),
|
||||||
|
"email_ids": [{
|
||||||
|
"email_id": contact_data.get("email"),
|
||||||
|
"is_primary": 1
|
||||||
|
}],
|
||||||
|
"phone_nos": [{
|
||||||
|
"phone": contact_data.get("phone_number"),
|
||||||
|
"is_primary_mobile_no": 1,
|
||||||
|
"is_primary_phone": 1
|
||||||
|
}]
|
||||||
}).insert(ignore_permissions=True)
|
}).insert(ignore_permissions=True)
|
||||||
print("Created new contact:", contact_doc.as_dict())
|
print("Created new contact:", contact_doc.as_dict())
|
||||||
else:
|
else:
|
||||||
@ -316,6 +324,7 @@ def upsert_client(data):
|
|||||||
# Contact -> Customer & Address
|
# Contact -> Customer & Address
|
||||||
print("#####DEBUG: Linking contacts to customer.")
|
print("#####DEBUG: Linking contacts to customer.")
|
||||||
for contact_doc in contact_docs:
|
for contact_doc in contact_docs:
|
||||||
|
contact_doc.address = address_doc.name
|
||||||
contact_doc.append("links", {
|
contact_doc.append("links", {
|
||||||
"link_doctype": "Customer",
|
"link_doctype": "Customer",
|
||||||
"link_name": customer_doc.name
|
"link_name": customer_doc.name
|
||||||
|
|||||||
@ -66,12 +66,6 @@ def get_estimate(estimate_name):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return build_error_response(str(e), 500)
|
return build_error_response(str(e), 500)
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def upsert_estimate(data):
|
|
||||||
"""Create or update an estimate."""
|
|
||||||
# TODO: Implement estimate creation/update logic
|
|
||||||
pass
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_estimate_items():
|
def get_estimate_items():
|
||||||
items = frappe.db.get_all("Quotation Item", fields=["*"])
|
items = frappe.db.get_all("Quotation Item", fields=["*"])
|
||||||
@ -98,25 +92,54 @@ def get_estimate_from_address(full_address):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def upsert_estimate(data):
|
def upsert_estimate(data):
|
||||||
"""Create or update an estimate."""
|
"""Create or update an estimate."""
|
||||||
print("DOIFJSEOFJISLFK")
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(data) if isinstance(data, str) else data
|
data = json.loads(data) if isinstance(data, str) else data
|
||||||
print("DEBUG: Retrieved address name:", data.get("address_name"))
|
print("DEBUG: Upsert estimate data:", data)
|
||||||
new_estimate = frappe.get_doc({
|
|
||||||
"doctype": "Quotation",
|
estimate_name = data.get("estimate_name")
|
||||||
"custom_installation_address": data.get("address_name"),
|
|
||||||
"contact_email": data.get("contact_email"),
|
# If estimate_name exists, update existing estimate
|
||||||
"party_name": data.get("contact_name"),
|
if estimate_name:
|
||||||
"customer_name": data.get("customer_name"),
|
print(f"DEBUG: Updating existing estimate: {estimate_name}")
|
||||||
})
|
estimate = frappe.get_doc("Quotation", estimate_name)
|
||||||
for item in data.get("items", []):
|
|
||||||
item = json.loads(item) if isinstance(item, str) else item
|
# Update fields
|
||||||
new_estimate.append("items", {
|
estimate.custom_installation_address = data.get("address_name")
|
||||||
"item_code": item.get("item_code"),
|
estimate.party_name = data.get("contact_name")
|
||||||
"qty": item.get("qty"),
|
|
||||||
|
# Clear existing items and add new ones
|
||||||
|
estimate.items = []
|
||||||
|
for item in data.get("items", []):
|
||||||
|
item = json.loads(item) if isinstance(item, str) else item
|
||||||
|
estimate.append("items", {
|
||||||
|
"item_code": item.get("item_code"),
|
||||||
|
"qty": item.get("qty"),
|
||||||
|
})
|
||||||
|
|
||||||
|
estimate.save()
|
||||||
|
print(f"DEBUG: Estimate updated: {estimate.name}")
|
||||||
|
return build_success_response(estimate.as_dict())
|
||||||
|
|
||||||
|
# Otherwise, create new estimate
|
||||||
|
else:
|
||||||
|
print("DEBUG: Creating new estimate")
|
||||||
|
print("DEBUG: Retrieved address name:", data.get("address_name"))
|
||||||
|
new_estimate = frappe.get_doc({
|
||||||
|
"doctype": "Quotation",
|
||||||
|
"custom_installation_address": data.get("address_name"),
|
||||||
|
"contact_email": data.get("contact_email"),
|
||||||
|
"party_name": data.get("contact_name"),
|
||||||
|
"customer_name": data.get("customer_name"),
|
||||||
})
|
})
|
||||||
new_estimate.insert()
|
for item in data.get("items", []):
|
||||||
print("DEBUG: New estimate created with name:", new_estimate.name)
|
item = json.loads(item) if isinstance(item, str) else item
|
||||||
return build_success_response(new_estimate.as_dict())
|
new_estimate.append("items", {
|
||||||
|
"item_code": item.get("item_code"),
|
||||||
|
"qty": item.get("qty"),
|
||||||
|
})
|
||||||
|
new_estimate.insert()
|
||||||
|
print("DEBUG: New estimate created with name:", new_estimate.name)
|
||||||
|
return build_success_response(new_estimate.as_dict())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(f"DEBUG: Error in upsert_estimate: {str(e)}")
|
||||||
return build_error_response(str(e), 500)
|
return build_error_response(str(e), 500)
|
||||||
@ -269,41 +269,11 @@ const handleLazyLoad = async (event) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cache when filters or sorting are active to ensure fresh data
|
|
||||||
const hasActiveFilters = Object.keys(filters).length > 0;
|
|
||||||
const hasActiveSorting = event.sortField && event.sortOrder;
|
|
||||||
if (hasActiveFilters || hasActiveSorting) {
|
|
||||||
paginationStore.clearTableCache("clients");
|
|
||||||
}
|
|
||||||
|
|
||||||
// For cache key, use primary sort field/order for compatibility
|
// For cache key, use primary sort field/order for compatibility
|
||||||
const primarySortField = filtersStore.getPrimarySortField("clients") || event.sortField;
|
const primarySortField = filtersStore.getPrimarySortField("clients") || event.sortField;
|
||||||
const primarySortOrder = filtersStore.getPrimarySortOrder("clients") || event.sortOrder;
|
const primarySortOrder = filtersStore.getPrimarySortOrder("clients") || event.sortOrder;
|
||||||
|
|
||||||
// Check cache first
|
// Always fetch fresh data from API (cache only stores pagination/filter/sort state, not data)
|
||||||
const cachedData = paginationStore.getCachedPage(
|
|
||||||
"clients",
|
|
||||||
paginationParams.page,
|
|
||||||
paginationParams.pageSize,
|
|
||||||
primarySortField,
|
|
||||||
primarySortOrder,
|
|
||||||
filters,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (cachedData) {
|
|
||||||
// Use cached data
|
|
||||||
tableData.value = cachedData.records;
|
|
||||||
totalRecords.value = cachedData.totalRecords;
|
|
||||||
paginationStore.setTotalRecords("clients", cachedData.totalRecords);
|
|
||||||
|
|
||||||
console.log("Loaded from cache:", {
|
|
||||||
records: cachedData.records.length,
|
|
||||||
total: cachedData.totalRecords,
|
|
||||||
page: paginationParams.page + 1,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call API with pagination, filters, and sorting in backend format
|
// Call API with pagination, filters, and sorting in backend format
|
||||||
console.log("Making API call with:", {
|
console.log("Making API call with:", {
|
||||||
paginationParams,
|
paginationParams,
|
||||||
@ -325,19 +295,6 @@ const handleLazyLoad = async (event) => {
|
|||||||
|
|
||||||
// Update pagination store with new total
|
// Update pagination store with new total
|
||||||
paginationStore.setTotalRecords("clients", result.pagination.total);
|
paginationStore.setTotalRecords("clients", result.pagination.total);
|
||||||
// Cache the result using primary sort for compatibility
|
|
||||||
paginationStore.setCachedPage(
|
|
||||||
"clients",
|
|
||||||
paginationParams.page,
|
|
||||||
paginationParams.pageSize,
|
|
||||||
primarySortField,
|
|
||||||
primarySortOrder,
|
|
||||||
filters,
|
|
||||||
{
|
|
||||||
records: result.data,
|
|
||||||
totalRecords: result.pagination.total,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading client data:", error);
|
console.error("Error loading client data:", error);
|
||||||
// You could also show a toast or other error notification here
|
// You could also show a toast or other error notification here
|
||||||
|
|||||||
@ -46,7 +46,7 @@
|
|||||||
fluid
|
fluid
|
||||||
/>
|
/>
|
||||||
<div v-if="selectedContact" class="verification-info">
|
<div v-if="selectedContact" class="verification-info">
|
||||||
<strong>Email:</strong> {{ selectedContact.customEmail || "N/A" }} <br />
|
<strong>Email:</strong> {{ selectedContact.emailId || "N/A" }} <br />
|
||||||
<strong>Phone:</strong> {{ selectedContact.phone || "N/A" }} <br />
|
<strong>Phone:</strong> {{ selectedContact.phone || "N/A" }} <br />
|
||||||
<strong>Primary Contact:</strong>
|
<strong>Primary Contact:</strong>
|
||||||
{{ selectedContact.isPrimaryContact ? "Yes" : "No" }}
|
{{ selectedContact.isPrimaryContact ? "Yes" : "No" }}
|
||||||
@ -87,13 +87,13 @@
|
|||||||
<div v-if="isEditable" class="action-buttons">
|
<div v-if="isEditable" class="action-buttons">
|
||||||
<Button label="Clear Items" @click="clearItems" severity="secondary" />
|
<Button label="Clear Items" @click="clearItems" severity="secondary" />
|
||||||
<Button
|
<Button
|
||||||
label="Submit"
|
label="Save Draft"
|
||||||
@click="showConfirmationModal = true"
|
@click="saveDraft"
|
||||||
:disabled="selectedItems.length === 0"
|
:disabled="selectedItems.length === 0"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div v-if="!isNew && estimate">
|
||||||
<Button label="Send Estimate" @click="showSubmitEstimateModal = true"/>
|
<Button label="Send Estimate" @click="showConfirmationModal = true"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -141,6 +141,10 @@
|
|||||||
fluid
|
fluid
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tip-section">
|
||||||
|
<i class="pi pi-info-circle"></i>
|
||||||
|
<span>Tip: Hold <kbd>Ctrl</kbd> (or <kbd>Cmd</kbd> on Mac) to select multiple items</span>
|
||||||
|
</div>
|
||||||
<DataTable
|
<DataTable
|
||||||
:data="filteredItems"
|
:data="filteredItems"
|
||||||
:columns="itemColumns"
|
:columns="itemColumns"
|
||||||
@ -158,8 +162,9 @@
|
|||||||
:visible="showConfirmationModal"
|
:visible="showConfirmationModal"
|
||||||
@update:visible="showConfirmationModal = $event"
|
@update:visible="showConfirmationModal = $event"
|
||||||
@close="showConfirmationModal = false"
|
@close="showConfirmationModal = false"
|
||||||
|
:options="{ showActions: false }"
|
||||||
>
|
>
|
||||||
<template #title>Confirm Estimate</template>
|
<template #title>Confirm Send Estimate</template>
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<h4>Does this information look correct?</h4>
|
<h4>Does this information look correct?</h4>
|
||||||
<p><strong>Address:</strong> {{ formData.address }}</p>
|
<p><strong>Address:</strong> {{ formData.address }}</p>
|
||||||
@ -171,6 +176,10 @@
|
|||||||
: ""
|
: ""
|
||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Email:</strong>
|
||||||
|
{{ selectedContact?.emailId || "N/A" }}
|
||||||
|
</p>
|
||||||
<p><strong>Items:</strong></p>
|
<p><strong>Items:</strong></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="item in selectedItems" :key="item.itemCode">
|
<li v-for="item in selectedItems" :key="item.itemCode">
|
||||||
@ -180,37 +189,18 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><strong>Total:</strong> ${{ totalCost.toFixed(2) }}</p>
|
<p><strong>Total:</strong> ${{ totalCost.toFixed(2) }}</p>
|
||||||
|
<p class="warning-text"><strong>⚠️ Warning:</strong> After sending this estimate, it will be locked and cannot be edited.</p>
|
||||||
<div class="confirmation-buttons">
|
<div class="confirmation-buttons">
|
||||||
<Button
|
<Button
|
||||||
label="No"
|
label="Cancel"
|
||||||
@click="showConfirmationModal = false"
|
@click="showConfirmationModal = false"
|
||||||
severity="secondary"
|
severity="secondary"
|
||||||
/>
|
/>
|
||||||
<Button label="Yes" @click="confirmSubmit" />
|
<Button label="Send Estimate" @click="confirmAndSendEstimate" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<!-- Submit Estimate Modal -->
|
|
||||||
<Modal
|
|
||||||
:visible="showSubmitEstimateModal"
|
|
||||||
@update:visible="showSubmitEstimateModal = $event"
|
|
||||||
@close="showSubmitEstimateModal = false"
|
|
||||||
:options="{ showActions: false }"
|
|
||||||
>
|
|
||||||
<p><strong>Submit Estimate and Email {{ selectedContact.firstName }} {{
|
|
||||||
selectedContact.lastName}}?</strong></p>
|
|
||||||
<p>This cannot be undone, please make sure all information is correct.</p>
|
|
||||||
<div class="confirmation-buttons">
|
|
||||||
<Button
|
|
||||||
label="No"
|
|
||||||
@click="showSubmitEstimateModal = false"
|
|
||||||
severity="secondary"
|
|
||||||
/>
|
|
||||||
<Button label="Yes" @click="sendEstimate" />
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -242,6 +232,7 @@ const formData = reactive({
|
|||||||
address: "",
|
address: "",
|
||||||
addressName: "",
|
addressName: "",
|
||||||
contact: "",
|
contact: "",
|
||||||
|
estimateName: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedAddress = ref(null);
|
const selectedAddress = ref(null);
|
||||||
@ -254,7 +245,6 @@ const selectedItems = ref([]);
|
|||||||
const showAddressModal = ref(false);
|
const showAddressModal = ref(false);
|
||||||
const showAddItemModal = ref(false);
|
const showAddItemModal = ref(false);
|
||||||
const showConfirmationModal = ref(false);
|
const showConfirmationModal = ref(false);
|
||||||
const showSubmitEstimateModal = ref(false);
|
|
||||||
const addressSearchResults = ref([]);
|
const addressSearchResults = ref([]);
|
||||||
const itemSearchTerm = ref("");
|
const itemSearchTerm = ref("");
|
||||||
|
|
||||||
@ -355,33 +345,37 @@ const updateTotal = () => {
|
|||||||
// Computed will update
|
// Computed will update
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmSubmit = async () => {
|
const saveDraft = async () => {
|
||||||
isSubmitting.value = true;
|
isSubmitting.value = true;
|
||||||
showConfirmationModal.value = false;
|
|
||||||
try {
|
try {
|
||||||
const data = {
|
const data = {
|
||||||
addressName: formData.addressName,
|
addressName: formData.addressName,
|
||||||
contactName: selectedContact.value.name,
|
contactName: selectedContact.value.name,
|
||||||
items: selectedItems.value.map((i) => ({ itemCode: i.itemCode, qty: i.qty })),
|
items: selectedItems.value.map((i) => ({ itemCode: i.itemCode, qty: i.qty })),
|
||||||
|
estimateName: formData.estimateName,
|
||||||
};
|
};
|
||||||
await Api.createEstimate(data);
|
estimate.value = await Api.createEstimate(data);
|
||||||
notificationStore.addSuccess("Estimate created successfully", "success");
|
notificationStore.addSuccess(
|
||||||
|
formData.estimateName ? "Estimate updated successfully" : "Estimate created successfully",
|
||||||
|
"success"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Redirect to view mode (remove new param)
|
||||||
router.push(`/estimate?address=${encodeURIComponent(formData.address)}`);
|
router.push(`/estimate?address=${encodeURIComponent(formData.address)}`);
|
||||||
// Reset form
|
|
||||||
formData.address = "";
|
|
||||||
formData.addressName = "";
|
|
||||||
formData.contact = "";
|
|
||||||
selectedAddress.value = null;
|
|
||||||
selectedContact.value = null;
|
|
||||||
selectedItems.value = [];
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating estimate:", error);
|
console.error("Error saving estimate:", error);
|
||||||
notificationStore.addError("Failed to create estimate", "error");
|
notificationStore.addNotification("Failed to save estimate", "error");
|
||||||
} finally {
|
} finally {
|
||||||
isSubmitting.value = false;
|
isSubmitting.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const confirmAndSendEstimate = async () => {
|
||||||
|
showConfirmationModal.value = false;
|
||||||
|
// TODO: Implement send estimate functionality
|
||||||
|
notificationStore.addWarning("Send estimate functionality coming soon");
|
||||||
|
};
|
||||||
|
|
||||||
const tableActions = [
|
const tableActions = [
|
||||||
{
|
{
|
||||||
label: "Add Selected Items",
|
label: "Add Selected Items",
|
||||||
@ -420,6 +414,67 @@ watch(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Watch for query param changes to refresh page behavior
|
||||||
|
watch(
|
||||||
|
() => route.query,
|
||||||
|
async (newQuery, oldQuery) => {
|
||||||
|
// If 'new' param or address changed, reload component state
|
||||||
|
if (newQuery.new !== oldQuery.new || newQuery.address !== oldQuery.address) {
|
||||||
|
// Reset all state
|
||||||
|
formData.address = "";
|
||||||
|
formData.addressName = "";
|
||||||
|
formData.contact = "";
|
||||||
|
formData.estimateName = null;
|
||||||
|
selectedAddress.value = null;
|
||||||
|
selectedContact.value = null;
|
||||||
|
contacts.value = [];
|
||||||
|
contactOptions.value = [];
|
||||||
|
selectedItems.value = [];
|
||||||
|
estimate.value = null;
|
||||||
|
|
||||||
|
// Reload data based on new query params
|
||||||
|
const newIsNew = newQuery.new === "true";
|
||||||
|
const newAddressQuery = newQuery.address;
|
||||||
|
|
||||||
|
if (newAddressQuery && newIsNew) {
|
||||||
|
// Creating new estimate - pre-fill address
|
||||||
|
await selectAddress(newAddressQuery);
|
||||||
|
} else if (newAddressQuery && !newIsNew) {
|
||||||
|
// Viewing existing estimate - load and populate all fields
|
||||||
|
try {
|
||||||
|
estimate.value = await Api.getEstimateFromAddress(newAddressQuery);
|
||||||
|
|
||||||
|
if (estimate.value) {
|
||||||
|
formData.estimateName = estimate.value.name;
|
||||||
|
await selectAddress(newAddressQuery);
|
||||||
|
formData.contact = estimate.value.partyName;
|
||||||
|
selectedContact.value = contacts.value.find((c) => c.name === estimate.value.partyName) || null;
|
||||||
|
|
||||||
|
if (estimate.value.items && estimate.value.items.length > 0) {
|
||||||
|
selectedItems.value = estimate.value.items.map(item => {
|
||||||
|
const fullItem = quotationItems.value.find(qi => qi.itemCode === item.itemCode);
|
||||||
|
return {
|
||||||
|
itemCode: item.itemCode,
|
||||||
|
itemName: item.itemName,
|
||||||
|
qty: item.qty,
|
||||||
|
standardRate: item.rate || fullItem?.standardRate || 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading estimate:", error);
|
||||||
|
notificationStore.addNotification(
|
||||||
|
"Failed to load estimate details.",
|
||||||
|
"error"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
console.log("DEBUG: Query params:", route.query);
|
console.log("DEBUG: Query params:", route.query);
|
||||||
try {
|
try {
|
||||||
@ -438,6 +493,9 @@ onMounted(async () => {
|
|||||||
console.log("DEBUG: Loaded estimate:", estimate.value);
|
console.log("DEBUG: Loaded estimate:", estimate.value);
|
||||||
|
|
||||||
if (estimate.value) {
|
if (estimate.value) {
|
||||||
|
// Set the estimate name for upserting
|
||||||
|
formData.estimateName = estimate.value.name;
|
||||||
|
|
||||||
await selectAddress(addressQuery);
|
await selectAddress(addressQuery);
|
||||||
// Set the contact from the estimate
|
// Set the contact from the estimate
|
||||||
formData.contact = estimate.value.partyName;
|
formData.contact = estimate.value.partyName;
|
||||||
@ -549,6 +607,33 @@ onMounted(async () => {
|
|||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tip-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
background-color: #e3f2fd;
|
||||||
|
border: 1px solid #2196f3;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #1565c0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-section i {
|
||||||
|
color: #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-section kbd {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.85em;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.confirmation-buttons {
|
.confirmation-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
@ -556,6 +641,15 @@ onMounted(async () => {
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning-text {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background-color: #fff3cd;
|
||||||
|
border: 1px solid #ffc107;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #856404;
|
||||||
|
}
|
||||||
|
|
||||||
.address-search-results {
|
.address-search-results {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,37 +128,7 @@ const handleLazyLoad = async (event) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cache when filters or sorting are active to ensure fresh data
|
// Always fetch fresh data from API (cache only stores pagination/filter/sort state, not data)
|
||||||
const hasActiveFilters = Object.keys(filters).length > 0;
|
|
||||||
const hasActiveSorting = paginationParams.sortField && paginationParams.sortOrder;
|
|
||||||
if (hasActiveFilters || hasActiveSorting) {
|
|
||||||
paginationStore.clearTableCache("estimates");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check cache first
|
|
||||||
const cachedData = paginationStore.getCachedPage(
|
|
||||||
"estimates",
|
|
||||||
paginationParams.page,
|
|
||||||
paginationParams.pageSize,
|
|
||||||
sorting.field || paginationParams.sortField,
|
|
||||||
sorting.order || paginationParams.sortOrder,
|
|
||||||
filters,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (cachedData) {
|
|
||||||
// Use cached data
|
|
||||||
tableData.value = cachedData.records;
|
|
||||||
totalRecords.value = cachedData.totalRecords;
|
|
||||||
paginationStore.setTotalRecords("estimates", cachedData.totalRecords);
|
|
||||||
|
|
||||||
console.log("Loaded from cache:", {
|
|
||||||
records: cachedData.records.length,
|
|
||||||
total: cachedData.totalRecords,
|
|
||||||
page: paginationParams.page + 1,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Making API call with:", { paginationParams, filters });
|
console.log("Making API call with:", { paginationParams, filters });
|
||||||
|
|
||||||
// Call API with pagination, filters, and sorting
|
// Call API with pagination, filters, and sorting
|
||||||
@ -180,20 +150,6 @@ const handleLazyLoad = async (event) => {
|
|||||||
storeTotalPages: paginationStore.getTotalPages("estimates"),
|
storeTotalPages: paginationStore.getTotalPages("estimates"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cache the result
|
|
||||||
paginationStore.setCachedPage(
|
|
||||||
"estimates",
|
|
||||||
paginationParams.page,
|
|
||||||
paginationParams.pageSize,
|
|
||||||
sorting.field || paginationParams.sortField,
|
|
||||||
sorting.order || paginationParams.sortOrder,
|
|
||||||
filters,
|
|
||||||
{
|
|
||||||
records: result.data,
|
|
||||||
totalRecords: result.pagination.total,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("Loaded from API:", {
|
console.log("Loaded from API:", {
|
||||||
records: result.data.length,
|
records: result.data.length,
|
||||||
total: result.pagination.total,
|
total: result.pagination.total,
|
||||||
|
|||||||
@ -75,37 +75,7 @@ const handleLazyLoad = async (event) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cache when filters or sorting are active to ensure fresh data
|
// Always fetch fresh data from API (cache only stores pagination/filter/sort state, not data)
|
||||||
const hasActiveFilters = Object.keys(filters).length > 0;
|
|
||||||
const hasActiveSorting = paginationParams.sortField && paginationParams.sortOrder;
|
|
||||||
if (hasActiveFilters || hasActiveSorting) {
|
|
||||||
paginationStore.clearTableCache("invoices");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check cache first
|
|
||||||
const cachedData = paginationStore.getCachedPage(
|
|
||||||
"invoices",
|
|
||||||
paginationParams.page,
|
|
||||||
paginationParams.pageSize,
|
|
||||||
sorting.field || paginationParams.sortField,
|
|
||||||
sorting.order || paginationParams.sortOrder,
|
|
||||||
filters,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (cachedData) {
|
|
||||||
// Use cached data
|
|
||||||
tableData.value = cachedData.records;
|
|
||||||
totalRecords.value = cachedData.totalRecords;
|
|
||||||
paginationStore.setTotalRecords("invoices", cachedData.totalRecords);
|
|
||||||
|
|
||||||
console.log("Loaded from cache:", {
|
|
||||||
records: cachedData.records.length,
|
|
||||||
total: cachedData.totalRecords,
|
|
||||||
page: paginationParams.page + 1,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Making API call with:", { paginationParams, filters });
|
console.log("Making API call with:", { paginationParams, filters });
|
||||||
|
|
||||||
// Call API with pagination, filters, and sorting
|
// Call API with pagination, filters, and sorting
|
||||||
@ -127,20 +97,6 @@ const handleLazyLoad = async (event) => {
|
|||||||
storeTotalPages: paginationStore.getTotalPages("invoices"),
|
storeTotalPages: paginationStore.getTotalPages("invoices"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cache the result
|
|
||||||
paginationStore.setCachedPage(
|
|
||||||
"invoices",
|
|
||||||
paginationParams.page,
|
|
||||||
paginationParams.pageSize,
|
|
||||||
sorting.field || paginationParams.sortField,
|
|
||||||
sorting.order || paginationParams.sortOrder,
|
|
||||||
filters,
|
|
||||||
{
|
|
||||||
records: result.data,
|
|
||||||
totalRecords: result.pagination.total,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("Loaded from API:", {
|
console.log("Loaded from API:", {
|
||||||
records: result.data.length,
|
records: result.data.length,
|
||||||
total: result.pagination.total,
|
total: result.pagination.total,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user