estimate view

This commit is contained in:
Casey 2025-12-02 12:27:58 -06:00
parent 7c738ef9f9
commit 0663cd2d8c
4 changed files with 116 additions and 23 deletions

View file

@ -1,6 +1,6 @@
<template>
<div class="estimate-page">
<h2>Create Estimate</h2>
<h2>{{ isNew ? 'Create Estimate' : 'View Estimate' }}</h2>
<!-- Address Section -->
<div class="address-section">
@ -13,13 +13,14 @@
id="address"
v-model="formData.address"
placeholder="Enter address to search"
:disabled="!isNew"
fluid
/>
<Button
label="Search"
icon="pi pi-search"
@click="searchAddresses"
:disabled="!formData.address.trim()"
:disabled="!formData.address.trim() || !isNew"
class="search-button"
/>
</div>
@ -41,7 +42,7 @@
optionLabel="label"
optionValue="value"
placeholder="Select a contact"
:disabled="!formData.address"
:disabled="!formData.address || !isEditable"
fluid
/>
<div v-if="selectedContact" class="verification-info">
@ -55,24 +56,35 @@
<!-- Items Section -->
<div class="items-section">
<h3>Items</h3>
<Button label="Add Item" icon="pi pi-plus" @click="showAddItemModal = true" />
<Button
v-if="isEditable"
label="Add Item"
icon="pi pi-plus"
@click="showAddItemModal = true"
/>
<div v-for="(item, index) in selectedItems" :key="item.itemCode" class="item-row">
<span>{{ item.itemName }}</span>
<InputNumber
v-model="item.qty"
:min="1"
:disabled="!isEditable"
showButtons
buttonLayout="horizontal"
@input="updateTotal"
/>
<span>Price: ${{ (item.standardRate || 0).toFixed(2) }}</span>
<span>Total: ${{ ((item.qty || 0) * (item.standardRate || 0)).toFixed(2) }}</span>
<Button icon="pi pi-trash" @click="removeItem(index)" severity="danger" />
<Button
v-if="isEditable"
icon="pi pi-trash"
@click="removeItem(index)"
severity="danger"
/>
</div>
<div class="total-section">
<strong>Total Cost: ${{ totalCost.toFixed(2) }}</strong>
</div>
<div class="action-buttons">
<div v-if="isEditable" class="action-buttons">
<Button label="Clear Items" @click="clearItems" severity="secondary" />
<Button
label="Submit"
@ -198,6 +210,7 @@ const loadingStore = useLoadingStore();
const notificationStore = useNotificationStore();
const addressQuery = route.query.address;
const isNew = route.query.new === "true" ? true : false;
const isSubmitting = ref(false);
@ -220,6 +233,16 @@ const showConfirmationModal = ref(false);
const addressSearchResults = ref([]);
const itemSearchTerm = ref("");
const estimate = ref(null);
// Computed property to determine if fields are editable
const isEditable = computed(() => {
if (isNew) return true;
if (!estimate.value) return false;
// If docstatus is 0 (draft), allow editing of contact and items
return estimate.value.docstatus === 0;
});
const itemColumns = [
{ label: "Item Code", fieldName: "itemCode", type: "text" },
{ label: "Item Name", fieldName: "itemName", type: "text" },
@ -263,7 +286,9 @@ const selectAddress = async (address) => {
value: c.name,
}));
const primary = contacts.value.find((c) => c.isPrimaryContact);
formData.contact = primary ? primary.name : contacts.value[0]?.name || "";
console.log("DEBUG: Selected address contacts:", contacts.value);
const existingContactName = estimate.value ? contacts.value.find((c) => c.fullName === estimate.value.partyName)?.name || "" : null;
formData.contact = estimate.value ? existingContactName : primary ? primary.name : contacts.value[0]?.name || "";
showAddressModal.value = false;
};
@ -371,13 +396,49 @@ watch(
);
onMounted(async () => {
console.log("DEBUG: Query params:", route.query);
try {
quotationItems.value = await Api.getQuotationItems();
} catch (error) {
console.error("Error loading quotation items:", error);
}
if (addressQuery) {
if (addressQuery && isNew) {
// Creating new estimate - pre-fill address
await selectAddress(addressQuery);
} else if (addressQuery && !isNew) {
// Viewing existing estimate - load and populate all fields
try {
estimate.value = await Api.getEstimateFromAddress(addressQuery);
console.log("DEBUG: Loaded estimate:", estimate.value);
if (estimate.value) {
await selectAddress(addressQuery);
// Set the contact from the estimate
formData.contact = estimate.value.partyName;
selectedContact.value = contacts.value.find((c) => c.name === estimate.value.partyName) || null;
// Populate items from the estimate
if (estimate.value.items && estimate.value.items.length > 0) {
selectedItems.value = estimate.value.items.map(item => {
// Find the full item details from quotationItems
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"
);
}
}
});
</script>
@ -435,6 +496,11 @@ onMounted(async () => {
border-radius: 4px;
}
/* When viewing (not editing), adjust grid to remove delete button column */
.estimate-page:has(h2:contains("View")) .item-row {
grid-template-columns: 2fr 1fr auto auto;
}
.total-section {
margin-top: 1rem;
font-size: 1.2rem;

View file

@ -4,6 +4,7 @@
<DataTable
:data="tableData"
:columns="columns"
:tableActions="tableActions"
tableName="estimates"
:lazy="true"
:totalRecords="totalRecords"
@ -38,10 +39,12 @@ import Api from "../../api";
import { useLoadingStore } from "../../stores/loading";
import { usePaginationStore } from "../../stores/pagination";
import { useFiltersStore } from "../../stores/filters";
import { useRouter } from "vue-router";
const loadingStore = useLoadingStore();
const paginationStore = usePaginationStore();
const filtersStore = useFiltersStore();
const router = useRouter();
const tableData = ref([]);
const totalRecords = ref(0);
@ -53,7 +56,7 @@ const filteredItems= []
// End junk
const columns = [
{ label: "Estimate ID", fieldName: "name", type: "text", sortable: true, filterable: true },
{ label: "Estimate Address", fieldName: "address", type: "text", sortable: true, filterable: true },
//{ label: "Address", fieldName: "customInstallationAddress", type: "text", sortable: true },
{ label: "Customer", fieldName: "customer", type: "text", sortable: true, filterable: true },
{
@ -70,8 +73,26 @@ const columns = [
//{ label: "Estimate Amount", fieldName:
];
const tableActions = [
{
label: "View Details",
action: (rowData) => {
router.push(`/estimate?address=${encodeURIComponent(rowData.address)}`);
},
type: "button",
style: "info",
icon: "pi pi-eye",
requiresSelection: true,
layout: {
position: "center",
variant: "outlined",
},
},
];
const handleEstimateClick = (status, rowData) => {
showSubmitEstimateModal.value = true;
// Navigate to estimate details page with the address
router.push(`/estimate?address=${encodeURIComponent(rowData.address)}`);
};
const closeSubmitEstimateModal = () => {