add accept estimate without sent

This commit is contained in:
Casey 2026-02-23 12:42:00 -06:00
parent c56f1a2930
commit 9f9318ddef
3 changed files with 139 additions and 7 deletions

View File

@ -313,15 +313,11 @@ def manual_response(name, response):
if not frappe.db.exists("Quotation", name):
raise Exception("Estimate not found.")
estimate = frappe.get_doc("Quotation", name)
if estimate.docstatus != 1:
raise Exception("Estimate must be submitted to update response.")
accepted = True if response == "Accepted" else False
new_status = "Estimate Accepted" if accepted else "Lost"
estimate.custom_response = response
estimate.custom_current_status = new_status
# estimate.custom_current_status = new_status
# estimate.status = "Ordered" if accepted else "Closed"
estimate.flags.ignore_permissions = True
print("DEBUG: Updating estimate with response:", response, "and status:", new_status)
estimate.save()

View File

@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""Move primary_contact from addresses.json to address_updates.json as {first_name, last_name}."""
import json
DATA_DIR = "/home/casey-wittrock/brotherton-bench/apps/custom_ui/custom_ui/migration_data"
def extract_full_name(primary_contact_str):
"""Extract full_name from the 'full_name-full_name' format."""
if not primary_contact_str:
return None
# Format is "FullName-FullName" where the name is duplicated
n = len(primary_contact_str)
if n >= 3:
mid = n // 2
if primary_contact_str[mid] == '-' and primary_contact_str[:mid] == primary_contact_str[mid + 1:]:
return primary_contact_str[:mid]
# Fallback: return as-is
return primary_contact_str
def main():
# Load contacts.json for first_name/last_name lookup
print("Loading contacts.json...")
with open(f"{DATA_DIR}/contacts.json", "r") as f:
contacts = json.load(f)
contact_lookup = {}
for c in contacts:
first = c.get("first_name", "")
last = c.get("last_name", "")
full = f"{first} {last}".strip()
if full:
contact_lookup[full] = {"first_name": first, "last_name": last}
print(f" Built contact lookup with {len(contact_lookup)} entries")
# Load addresses.json
print("Loading addresses.json...")
with open(f"{DATA_DIR}/addresses.json", "r") as f:
addresses = json.load(f)
# Build map: address_title -> primary_contact info
primary_contact_map = {}
missing_contacts = set()
null_contacts = 0
found_contacts = 0
for addr in addresses:
title = addr.get("address_title")
pc_str = addr.get("primary_contact")
if not title:
continue
if not pc_str:
null_contacts += 1
continue
full_name = extract_full_name(pc_str)
if full_name and full_name in contact_lookup:
primary_contact_map[title] = contact_lookup[full_name]
found_contacts += 1
elif full_name:
missing_contacts.add(full_name)
# Still use the full_name as first_name fallback
primary_contact_map[title] = {"first_name": full_name, "last_name": ""}
print(f" Found {found_contacts} primary contacts, {null_contacts} null, {len(missing_contacts)} missing from contacts.json")
if missing_contacts:
for c in sorted(missing_contacts)[:5]:
print(f" - {c}")
if len(missing_contacts) > 5:
print(f" ... and {len(missing_contacts) - 5} more")
# Remove primary_contact from addresses.json
print("\nRemoving primary_contact from addresses.json...")
for addr in addresses:
addr.pop("primary_contact", None)
with open(f"{DATA_DIR}/addresses.json", "w") as f:
json.dump(addresses, f, indent=2)
print(" Done.")
# Add primary_contact to address_updates.json
print("Adding primary_contact to address_updates.json...")
with open(f"{DATA_DIR}/address_updates.json", "r") as f:
address_updates = json.load(f)
added = 0
for entry in address_updates:
title = entry.get("address_title")
if title and title in primary_contact_map:
entry["primary_contact"] = primary_contact_map[title]
added += 1
with open(f"{DATA_DIR}/address_updates.json", "w") as f:
json.dump(address_updates, f, indent=2)
print(f" Added primary_contact to {added} address update entries.")
print("\nDone!")
if __name__ == "__main__":
main()

View File

@ -2,8 +2,8 @@
<div class="estimate-page">
<h2>{{ isNew ? 'Create Estimate' : 'View Estimate' }}</h2>
<div v-if="!isNew && estimate" class="page-actions">
<div v-if="estimate && estimate.customSent === 1">
<Button label="Estimate Response" @click="showResponseModal = true" />
<div v-if="estimate">
<Button label="Estimate Response" @click="handleResponseClick" />
</div>
<Button label="Duplicate" icon="pi pi-copy" @click="duplicateEstimate" />
</div>
@ -245,7 +245,7 @@
<div v-if="estimate">
<Button label="Send Estimate" @click="initiateSendEstimate" :disabled="estimate.customSent === 1"/>
</div>
<div v-if="estimate && estimate.customSent === 1" class="response-status">
<div v-if="estimate && (estimate.customSent === 1 || estimateResponse)" class="response-status">
<h4>Customer Response:</h4>
<span :class="getResponseClass(getResponseText(estimateResponse))">
{{ getResponseText(estimateResponse) }}
@ -270,6 +270,27 @@
<Button label="Submit" @click="submitResponse"/>
</Modal>
<!-- Unsent Estimate Warning Modal -->
<Modal
:visible="showUnsentWarningModal"
@update:visible="showUnsentWarningModal = $event"
@close="showUnsentWarningModal = false"
:options="{ showActions: false }"
>
<template #title>Warning</template>
<div class="modal-content">
<p>This estimate has not been sent. Are you sure you want to manually set a response?</p>
<div class="confirmation-buttons">
<Button
label="No"
@click="showUnsentWarningModal = false"
severity="secondary"
/>
<Button label="Yes" @click="confirmUnsentWarning" />
</div>
</div>
</Modal>
<!-- Save Template Modal -->
<SaveTemplateModal
:visible="showSaveTemplateModal"
@ -485,6 +506,7 @@ const showAddItemModal = ref(false);
const showConfirmationModal = ref(false);
const showDownPaymentWarningModal = ref(false);
const showResponseModal = ref(false);
const showUnsentWarningModal = ref(false);
const showSaveTemplateModal = ref(false);
const showSavePackageModal = ref(false);
const addressSearchResults = ref([]);
@ -820,6 +842,19 @@ const submitResponse = () => {
showResponseModal.value = false;
}
const handleResponseClick = () => {
if (estimate.value.customSent !== 1) {
showUnsentWarningModal.value = true;
} else {
showResponseModal.value = true;
}
};
const confirmUnsentWarning = () => {
showUnsentWarningModal.value = false;
showResponseModal.value = true;
};
const duplicateEstimate = () => {
if (!estimate.value) return;