Browse Source

feat: bulk document removal working except for Job Invoice

document-upload-removal-layout-update document-alpha
nic 12 months ago
parent
commit
6bd57c10be
  1. 495
      internal/handlers/web/document_remove.go
  2. 6
      internal/handlers/web/documents.go
  3. 18
      templates/partials/document_remove.html
  4. 8
      templates/partials/document_upload_form.html

495
internal/handlers/web/document_remove.go

@ -36,8 +36,8 @@ func DocumentRemoveHandler(w http.ResponseWriter, r *http.Request) {
"DocumentTypes": []map[string]string{ "DocumentTypes": []map[string]string{
{"value": "1", "label": "Job Paperwork"}, {"value": "1", "label": "Job Paperwork"},
{"value": "2", "label": "Job Vendor Bill"}, {"value": "2", "label": "Job Vendor Bill"},
{"value": "4", "label": "Generic Attachment"}, {"value": "7", "label": "Generic Attachment"},
{"value": "7", "label": "Blank Paperwork"}, {"value": "10", "label": "Blank Paperwork"},
{"value": "14", "label": "Job Invoice"}, {"value": "14", "label": "Job Invoice"},
}, },
} }
@ -326,15 +326,17 @@ func RemoveJobAttachmentsHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
// Delete the attachment // For all attachment types, we'll use the attachment endpoint for deletion
err = session.DeleteAttachment(id) // The API endpoint is /attachment/{id} as defined in DeleteAttachment method
log.Printf("Deleting attachment %s (ID: %s) using attachment endpoint", fileResult.Name, id)
deleteErr := session.DeleteAttachment(id)
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
if err != nil { if deleteErr != nil {
fileResult.Success = false fileResult.Success = false
fileResult.Error = err.Error() fileResult.Error = deleteErr.Error()
results.ErrorCount++ results.ErrorCount++
results.Success = false results.Success = false
} else { } else {
@ -564,6 +566,271 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) {
// Only proceed if we have access or couldn't determine access // Only proceed if we have access or couldn't determine access
if err != nil || hasAccess { if err != nil || hasAccess {
// Try job paperwork endpoint
if stringInSlice("1", docTypes) || len(docTypes) == 0 {
log.Printf("**** JOB %s: Trying the job paperwork endpoint", jobID)
paperworkURL := fmt.Sprintf("%s/job/%s/paperwork", api.BaseURL, jobID)
paperworkReq, err := http.NewRequest("GET", paperworkURL, nil)
if err == nil {
paperworkReq.Header.Set("Cookie", session.Cookie)
paperworkReq.Header.Set("Accept", "application/json")
log.Printf("**** JOB %s: Sending request to job paperwork endpoint: %s", jobID, paperworkURL)
paperworkResp, err := session.Client.Do(paperworkReq)
if err == nil && paperworkResp.StatusCode == http.StatusOK {
defer paperworkResp.Body.Close()
paperworkBody, _ := io.ReadAll(paperworkResp.Body)
// Log preview of the response
responsePreview := string(paperworkBody)
if len(responsePreview) > 300 {
responsePreview = responsePreview[:300] + "... [truncated]"
}
log.Printf("**** JOB %s: Job paperwork response preview: %s", jobID, responsePreview)
var paperworkResult map[string]interface{}
if err := json.Unmarshal(paperworkBody, &paperworkResult); err == nil {
// Process objects array if it exists
if objects, ok := paperworkResult["objects"].([]interface{}); ok && len(objects) > 0 {
log.Printf("**** JOB %s: Found %d paperwork items in objects array", jobID, len(objects))
for _, obj := range objects {
if paperworkMap, ok := obj.(map[string]interface{}); ok {
// Set purposeId to 1 for job paperwork
paperworkMap["purposeId"] = float64(1)
attachments = append(attachments, paperworkMap)
log.Printf("**** JOB %s: Added job paperwork to attachments", jobID)
}
}
} else if data, ok := paperworkResult["data"].(map[string]interface{}); ok {
// Check in data for attachments
if attachmentsArray, ok := data["attachments"].([]interface{}); ok && len(attachmentsArray) > 0 {
log.Printf("**** JOB %s: Found %d paperwork items in data.attachments", jobID, len(attachmentsArray))
for _, att := range attachmentsArray {
if attMap, ok := att.(map[string]interface{}); ok {
// Ensure purposeId is set correctly
attMap["purposeId"] = float64(1)
attachments = append(attachments, attMap)
log.Printf("**** JOB %s: Added job paperwork from data.attachments", jobID)
}
}
}
// Also check other locations in data
possibleKeys := []string{"paperwork", "objects"}
for _, key := range possibleKeys {
if items, ok := data[key].([]interface{}); ok && len(items) > 0 {
log.Printf("**** JOB %s: Found %d paperwork items in data.%s", jobID, len(items), key)
for _, item := range items {
if itemMap, ok := item.(map[string]interface{}); ok {
// Set purposeId to 1 for job paperwork
itemMap["purposeId"] = float64(1)
attachments = append(attachments, itemMap)
log.Printf("**** JOB %s: Added job paperwork from data.%s", jobID, key)
}
}
}
}
}
}
} else {
log.Printf("**** JOB %s: Job paperwork endpoint failed or returned non-200 status: %v", jobID, err)
}
}
}
// Try job invoice endpoint
if stringInSlice("14", docTypes) || len(docTypes) == 0 {
log.Printf("**** JOB %s: Trying the job invoice endpoint", jobID)
invoiceURL := fmt.Sprintf("%s/job/%s/invoice", api.BaseURL, jobID)
invoiceReq, err := http.NewRequest("GET", invoiceURL, nil)
if err == nil {
invoiceReq.Header.Set("Cookie", session.Cookie)
invoiceReq.Header.Set("Accept", "application/json")
log.Printf("**** JOB %s: Sending request to job invoice endpoint: %s", jobID, invoiceURL)
invoiceResp, err := session.Client.Do(invoiceReq)
if err == nil && invoiceResp.StatusCode == http.StatusOK {
defer invoiceResp.Body.Close()
invoiceBody, _ := io.ReadAll(invoiceResp.Body)
// Log preview of the response
responsePreview := string(invoiceBody)
if len(responsePreview) > 300 {
responsePreview = responsePreview[:300] + "... [truncated]"
}
log.Printf("**** JOB %s: Job invoice response preview: %s", jobID, responsePreview)
var invoiceResult map[string]interface{}
if err := json.Unmarshal(invoiceBody, &invoiceResult); err == nil {
// Process objects array if it exists
if objects, ok := invoiceResult["objects"].([]interface{}); ok && len(objects) > 0 {
log.Printf("**** JOB %s: Found %d job invoices in objects array", jobID, len(objects))
for _, obj := range objects {
if invoiceMap, ok := obj.(map[string]interface{}); ok {
// Set purposeId to 14 for job invoices
invoiceMap["purposeId"] = float64(14)
attachments = append(attachments, invoiceMap)
log.Printf("**** JOB %s: Added job invoice to attachments", jobID)
}
}
} else if data, ok := invoiceResult["data"].(map[string]interface{}); ok {
// Check in data for attachments
possibleKeys := []string{"invoices", "attachments", "objects"}
for _, key := range possibleKeys {
if items, ok := data[key].([]interface{}); ok && len(items) > 0 {
log.Printf("**** JOB %s: Found %d invoices in data.%s", jobID, len(items), key)
for _, item := range items {
if itemMap, ok := item.(map[string]interface{}); ok {
// Set purposeId to 14 for job invoices
itemMap["purposeId"] = float64(14)
attachments = append(attachments, itemMap)
log.Printf("**** JOB %s: Added job invoice from data.%s", jobID, key)
}
}
}
}
}
}
} else {
log.Printf("**** JOB %s: Job invoice endpoint failed or returned non-200 status: %v", jobID, err)
}
}
}
// Try generic attachment endpoint
if stringInSlice("7", docTypes) || len(docTypes) == 0 {
log.Printf("**** JOB %s: Trying the generic attachment endpoint", jobID)
genericURL := fmt.Sprintf("%s/job/%s/attachment", api.BaseURL, jobID)
genericReq, err := http.NewRequest("GET", genericURL, nil)
if err == nil {
genericReq.Header.Set("Cookie", session.Cookie)
genericReq.Header.Set("Accept", "application/json")
log.Printf("**** JOB %s: Sending request to generic attachment endpoint: %s", jobID, genericURL)
genericResp, err := session.Client.Do(genericReq)
if err == nil && genericResp.StatusCode == http.StatusOK {
defer genericResp.Body.Close()
genericBody, _ := io.ReadAll(genericResp.Body)
// Log preview of the response
responsePreview := string(genericBody)
if len(responsePreview) > 300 {
responsePreview = responsePreview[:300] + "... [truncated]"
}
log.Printf("**** JOB %s: Generic attachment response preview: %s", jobID, responsePreview)
var genericResult map[string]interface{}
if err := json.Unmarshal(genericBody, &genericResult); err == nil {
// Process objects array if it exists
if objects, ok := genericResult["objects"].([]interface{}); ok && len(objects) > 0 {
log.Printf("**** JOB %s: Found %d generic attachments in objects array", jobID, len(objects))
for _, obj := range objects {
if attachMap, ok := obj.(map[string]interface{}); ok {
// Set purposeId to 7 for generic attachments
attachMap["purposeId"] = float64(7)
attachments = append(attachments, attachMap)
log.Printf("**** JOB %s: Added generic attachment to attachments", jobID)
}
}
} else if data, ok := genericResult["data"].(map[string]interface{}); ok {
// Check in data for attachments
possibleKeys := []string{"attachments", "objects"}
for _, key := range possibleKeys {
if items, ok := data[key].([]interface{}); ok && len(items) > 0 {
log.Printf("**** JOB %s: Found %d generic attachments in data.%s", jobID, len(items), key)
for _, item := range items {
if itemMap, ok := item.(map[string]interface{}); ok {
// Set purposeId to 7 for generic attachments
itemMap["purposeId"] = float64(7)
attachments = append(attachments, itemMap)
log.Printf("**** JOB %s: Added generic attachment from data.%s", jobID, key)
}
}
}
}
}
}
} else {
log.Printf("**** JOB %s: Generic attachment endpoint failed or returned non-200 status: %v", jobID, err)
}
}
}
// Try vendor bill endpoint
if stringInSlice("2", docTypes) || len(docTypes) == 0 {
log.Printf("**** JOB %s: Trying the vendor invoice endpoint", jobID)
vendorInvoiceURL := fmt.Sprintf("%s/job/%s/vendorinvoice", api.BaseURL, jobID)
vendorInvoiceReq, err := http.NewRequest("GET", vendorInvoiceURL, nil)
if err == nil {
vendorInvoiceReq.Header.Set("Cookie", session.Cookie)
vendorInvoiceReq.Header.Set("Accept", "application/json")
log.Printf("**** JOB %s: Sending request to vendor invoice endpoint: %s", jobID, vendorInvoiceURL)
vendorInvoiceResp, err := session.Client.Do(vendorInvoiceReq)
if err == nil && vendorInvoiceResp.StatusCode == http.StatusOK {
defer vendorInvoiceResp.Body.Close()
vendorInvoiceBody, _ := io.ReadAll(vendorInvoiceResp.Body)
// Log preview of the response
responsePreview := string(vendorInvoiceBody)
if len(responsePreview) > 300 {
responsePreview = responsePreview[:300] + "... [truncated]"
}
log.Printf("**** JOB %s: Vendor invoice response preview: %s", jobID, responsePreview)
var vendorInvoiceResult map[string]interface{}
if err := json.Unmarshal(vendorInvoiceBody, &vendorInvoiceResult); err == nil {
// Process objects array if it exists
if objects, ok := vendorInvoiceResult["objects"].([]interface{}); ok && len(objects) > 0 {
log.Printf("**** JOB %s: Found %d vendor invoices in objects array", jobID, len(objects))
for _, obj := range objects {
if invoiceMap, ok := obj.(map[string]interface{}); ok {
// Set purposeId to 2 for vendor bills
invoiceMap["purposeId"] = float64(2)
attachments = append(attachments, invoiceMap)
log.Printf("**** JOB %s: Added vendor invoice to attachments", jobID)
}
}
} else if data, ok := vendorInvoiceResult["data"].(map[string]interface{}); ok {
// Check in data for attachments
possibleKeys := []string{"invoices", "vendorInvoices", "attachments", "objects"}
for _, key := range possibleKeys {
if items, ok := data[key].([]interface{}); ok && len(items) > 0 {
log.Printf("**** JOB %s: Found %d vendor bills in data.%s", jobID, len(items), key)
for _, item := range items {
if itemMap, ok := item.(map[string]interface{}); ok {
// Set purposeId to 2 for vendor bills
itemMap["purposeId"] = float64(2)
attachments = append(attachments, itemMap)
log.Printf("**** JOB %s: Added vendor bill from data.%s", jobID, key)
}
}
}
}
}
}
} else {
log.Printf("**** JOB %s: Vendor invoice endpoint failed or returned non-200 status: %v", jobID, err)
}
}
}
// Then continue with general paperwork endpoint to catch any we might have missed
log.Printf("**** JOB %s: Trying general paperwork endpoint", jobID)
// Directly try to get paperwork using the specialized API // Directly try to get paperwork using the specialized API
log.Printf("**** JOB %s: Using specialized paperwork API", jobID) log.Printf("**** JOB %s: Using specialized paperwork API", jobID)
paperworkItems, err := session.GetJobPaperwork(jobID) paperworkItems, err := session.GetJobPaperwork(jobID)
@ -648,7 +915,117 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) {
if len(attachments) == 0 { if len(attachments) == 0 {
log.Printf("**** JOB %s: No attachments found yet, trying direct paperwork endpoint") log.Printf("**** JOB %s: No attachments found yet, trying direct paperwork endpoint")
// Make a direct call to the paperwork endpoint // Directly try to get vendor bills using a specific endpoint
log.Printf("**** JOB %s: Trying vendor bill specific endpoint", jobID)
vendorBillURL := fmt.Sprintf("%s/job/%s/vendor-bill", api.BaseURL, jobID)
vendorBillReq, err := http.NewRequest("GET", vendorBillURL, nil)
if err == nil {
vendorBillReq.Header.Set("Cookie", session.Cookie)
vendorBillReq.Header.Set("Accept", "application/json")
log.Printf("**** JOB %s: Sending request to vendor bill endpoint: %s", jobID, vendorBillURL)
vendorBillResp, err := session.Client.Do(vendorBillReq)
if err == nil && vendorBillResp.StatusCode == http.StatusOK {
defer vendorBillResp.Body.Close()
vendorBillBody, _ := io.ReadAll(vendorBillResp.Body)
// Log full response structure for debugging
log.Printf("**** JOB %s: Full vendor bill response: %s", jobID, string(vendorBillBody))
var vendorBillResult map[string]interface{}
if err := json.Unmarshal(vendorBillBody, &vendorBillResult); err == nil {
// Log all root keys in the response
rootKeys := make([]string, 0)
for k := range vendorBillResult {
rootKeys = append(rootKeys, k)
}
log.Printf("**** JOB %s: Vendor bill response root keys: %s",
jobID, strings.Join(rootKeys, ", "))
// Check if data exists and log all its keys
if data, ok := vendorBillResult["data"].(map[string]interface{}); ok {
dataKeys := make([]string, 0)
for k := range data {
dataKeys = append(dataKeys, k)
}
log.Printf("**** JOB %s: Vendor bill data keys: %s",
jobID, strings.Join(dataKeys, ", "))
// First try vendorBills directly
if vendorBills, ok := data["vendorBills"].([]interface{}); ok && len(vendorBills) > 0 {
log.Printf("**** JOB %s: Found %d vendor bills in data.vendorBills", jobID, len(vendorBills))
for _, bill := range vendorBills {
if billMap, ok := bill.(map[string]interface{}); ok {
// Set purposeId to 2 for vendor bills
billMap["purposeId"] = float64(2)
attachments = append(attachments, billMap)
}
}
} else {
// Try other possible locations
log.Printf("**** JOB %s: No vendorBills found in data, checking other locations", jobID)
// Try each possible location for the vendor bills
possibleKeys := []string{"objects", "attachments", "bills", "paperwork", "documents"}
for _, key := range possibleKeys {
if items, ok := data[key].([]interface{}); ok && len(items) > 0 {
log.Printf("**** JOB %s: Found %d items in data.%s", jobID, len(items), key)
// Log the structure of the first item
if itemMap, ok := items[0].(map[string]interface{}); ok {
itemKeys := make([]string, 0)
for k := range itemMap {
itemKeys = append(itemKeys, k)
}
log.Printf("**** JOB %s: First item in data.%s has keys: %s",
jobID, key, strings.Join(itemKeys, ", "))
// Log the first item as JSON for inspection
if itemJSON, err := json.Marshal(itemMap); err == nil {
log.Printf("**** JOB %s: First item in data.%s: %s",
jobID, key, string(itemJSON))
}
}
// Add all items as attachments
for _, item := range items {
if itemMap, ok := item.(map[string]interface{}); ok {
// Set purposeId to 2 for vendor bills
itemMap["purposeId"] = float64(2)
attachments = append(attachments, itemMap)
log.Printf("**** JOB %s: Added vendor bill from data.%s", jobID, key)
}
}
}
}
}
} else {
// If data is not a map, check for top-level objects
log.Printf("**** JOB %s: No data object in vendor bill response or it's not a map", jobID)
if objects, ok := vendorBillResult["objects"].([]interface{}); ok && len(objects) > 0 {
log.Printf("**** JOB %s: Found %d objects at root level", jobID, len(objects))
for _, obj := range objects {
if objMap, ok := obj.(map[string]interface{}); ok {
// Set purposeId to 2 for vendor bills
objMap["purposeId"] = float64(2)
attachments = append(attachments, objMap)
log.Printf("**** JOB %s: Added vendor bill from root.objects", jobID)
}
}
}
}
}
} else {
log.Printf("**** JOB %s: Vendor bill endpoint failed or returned non-200 status: %v", jobID, err)
}
}
// Also try direct paperwork endpoint
log.Printf("**** JOB %s: Trying direct paperwork endpoint", jobID)
paperworkURL := fmt.Sprintf("%s/job/%s/paperwork", api.BaseURL, jobID) paperworkURL := fmt.Sprintf("%s/job/%s/paperwork", api.BaseURL, jobID)
paperworkReq, err := http.NewRequest("GET", paperworkURL, nil) paperworkReq, err := http.NewRequest("GET", paperworkURL, nil)
if err == nil { if err == nil {
@ -720,6 +1097,14 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) {
// Log attachment count after direct endpoint // Log attachment count after direct endpoint
log.Printf("**** JOB %s: Attachments found after direct paperwork call: %d", jobID, len(attachments)) log.Printf("**** JOB %s: Attachments found after direct paperwork call: %d", jobID, len(attachments))
// Deduplicate attachments to avoid processing the same ones multiple times
originalCount := len(attachments)
attachments = deduplicateAttachments(attachments)
if len(attachments) < originalCount {
log.Printf("**** JOB %s: Removed %d duplicate attachments, %d unique attachments remain",
jobID, originalCount-len(attachments), len(attachments))
}
// Now actually apply the filters // Now actually apply the filters
filteredAttachments := make([]map[string]interface{}, 0) filteredAttachments := make([]map[string]interface{}, 0)
@ -800,14 +1185,17 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
// Get document name/filename // Get filename from any available field, don't assume description exists
var filename string var filename string
if nameVal, ok := attachment["fileName"].(string); ok { if desc, ok := attachment["description"].(string); ok && desc != "" {
filename = nameVal filename = desc
} else if nameVal, ok := attachment["name"].(string); ok { } else if name, ok := attachment["name"].(string); ok && name != "" {
filename = nameVal filename = name
} else if nameVal, ok := attachment["description"].(string); ok { } else if fname, ok := attachment["fileName"].(string); ok && fname != "" {
filename = nameVal filename = fname
} else {
// If no name is available, use the ID as the name
filename = fmt.Sprintf("Attachment ID: %s", attachment["id"])
} }
log.Printf("**** JOB %s: Attachment filename: %s", jobID, filename) log.Printf("**** JOB %s: Attachment filename: %s", jobID, filename)
@ -876,6 +1264,14 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) {
if len(attachments) == 0 { if len(attachments) == 0 {
log.Printf("**** JOB %s: WARNING! No attachments found after all retrieval attempts") log.Printf("**** JOB %s: WARNING! No attachments found after all retrieval attempts")
} else {
// Deduplicate again before continuing with deletion, in case multiple methods found the same attachments
originalCount := len(attachments)
attachments = deduplicateAttachments(attachments)
if len(attachments) < originalCount {
log.Printf("**** JOB %s: Final deduplication removed %d duplicates, %d unique attachments remain",
jobID, originalCount-len(attachments), len(attachments))
}
} }
} }
@ -890,14 +1286,17 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) {
} }
attachmentIDStr := fmt.Sprintf("%.0f", attachmentIDRaw) attachmentIDStr := fmt.Sprintf("%.0f", attachmentIDRaw)
// Get the filename // Safely get filename from any available field
var filename string var filename string
if nameVal, ok := attachment["fileName"].(string); ok { if desc, ok := attachment["description"].(string); ok && desc != "" {
filename = nameVal filename = desc
} else if nameVal, ok := attachment["name"].(string); ok { } else if name, ok := attachment["name"].(string); ok && name != "" {
filename = nameVal filename = name
} else if nameVal, ok := attachment["description"].(string); ok { } else if fname, ok := attachment["fileName"].(string); ok && fname != "" {
filename = nameVal filename = fname
} else {
// If no name is available, use the ID as the name
filename = fmt.Sprintf("Attachment ID: %s", attachmentIDStr)
} }
if filename == "" { if filename == "" {
@ -982,7 +1381,19 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) {
attachmentIDFloat := att["id"].(float64) attachmentIDFloat := att["id"].(float64)
attachmentIDStr := fmt.Sprintf("%.0f", attachmentIDFloat) // Convert to string without decimal attachmentIDStr := fmt.Sprintf("%.0f", attachmentIDFloat) // Convert to string without decimal
filename := att["description"].(string)
// Safely get filename from any available field
var filename string
if desc, ok := att["description"].(string); ok && desc != "" {
filename = desc
} else if name, ok := att["name"].(string); ok && name != "" {
filename = name
} else if fname, ok := att["fileName"].(string); ok && fname != "" {
filename = fname
} else {
// If no name is available, use the ID as the name
filename = fmt.Sprintf("Attachment ID: %s", attachmentIDStr)
}
fileResult := struct { fileResult := struct {
Name string Name string
@ -992,16 +1403,18 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) {
Name: filename, Name: filename,
} }
// Delete the attachment // For all attachment types, we'll use the attachment endpoint for deletion
err := session.DeleteAttachment(attachmentIDStr) // The API endpoint is /attachment/{id} as defined in DeleteAttachment method
log.Printf("Deleting attachment %s (ID: %s) using attachment endpoint", filename, attachmentIDStr)
deleteErr := session.DeleteAttachment(attachmentIDStr)
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
if err != nil { if deleteErr != nil {
fileResult.Success = false fileResult.Success = false
fileResult.Error = err.Error() fileResult.Error = deleteErr.Error()
log.Printf("Error deleting attachment %s: %v", filename, err) log.Printf("Error deleting attachment %s: %v", filename, deleteErr)
jobResult.Success = false jobResult.Success = false
} else { } else {
fileResult.Success = true fileResult.Success = true
@ -1174,3 +1587,31 @@ func logAttachmentDetails(jobID string, attachment map[string]interface{}) {
} }
log.Printf("***** END ATTACHMENT DETAILS *****") log.Printf("***** END ATTACHMENT DETAILS *****")
} }
// Helper function to deduplicate attachments based on ID
func deduplicateAttachments(attachments []map[string]interface{}) []map[string]interface{} {
seen := make(map[string]bool)
uniqueAttachments := make([]map[string]interface{}, 0)
for _, attachment := range attachments {
// Get the ID as a string for deduplication
var idStr string
if id, ok := attachment["id"].(float64); ok {
idStr = fmt.Sprintf("%.0f", id)
} else if id, ok := attachment["id"].(string); ok {
idStr = id
} else {
// If no valid ID, just add it (should not happen)
uniqueAttachments = append(uniqueAttachments, attachment)
continue
}
// Only add if we haven't seen this ID before
if !seen[idStr] {
seen[idStr] = true
uniqueAttachments = append(uniqueAttachments, attachment)
}
}
return uniqueAttachments
}

6
internal/handlers/web/documents.go

@ -555,9 +555,9 @@ func DocumentFieldAddHandler(w http.ResponseWriter, r *http.Request) {
<label>Document Type:</label> <label>Document Type:</label>
<select class="card-input" id="document-type-%s" name="document-type-%s"> <select class="card-input" id="document-type-%s" name="document-type-%s">
<option value="">Select Document Type</option> <option value="">Select Document Type</option>
<option value="01" selected>Job Paperwork</option> <option value="1" selected>Job Paperwork</option>
<option value="02">Job Vendor Bill</option> <option value="2">Job Vendor Bill</option>
<option value="07">Generic Attachment</option> <option value="7">Generic Attachment</option>
<option value="10">Blank Paperwork</option> <option value="10">Blank Paperwork</option>
<option value="14">Job Invoice</option> <option value="14">Job Invoice</option>
</select> </select>

18
templates/partials/document_remove.html

@ -27,29 +27,21 @@
<div class="form-group"> <div class="form-group">
<label>Document Types to Remove:</label> <label>Document Types to Remove:</label>
<div class="checkbox-group"> <div class="checkbox-group">
<div class="checkbox-item">
<input type="checkbox" id="type-0" name="documentTypes" value="0">
<label for="type-0">Job Paperwork</label>
</div>
<div class="checkbox-item"> <div class="checkbox-item">
<input type="checkbox" id="type-1" name="documentTypes" value="1"> <input type="checkbox" id="type-1" name="documentTypes" value="1">
<label for="type-1">Job Vendor Bill</label> <label for="type-1">Job Paperwork</label>
</div> </div>
<div class="checkbox-item"> <div class="checkbox-item">
<input type="checkbox" id="type-2" name="documentTypes" value="2"> <input type="checkbox" id="type-2" name="documentTypes" value="2">
<label for="type-2">Job Picture</label> <label for="type-2">Job Vendor Bill</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="type-4" name="documentTypes" value="4">
<label for="type-4">Generic Attachment</label>
</div> </div>
<div class="checkbox-item"> <div class="checkbox-item">
<input type="checkbox" id="type-7" name="documentTypes" value="7"> <input type="checkbox" id="type-7" name="documentTypes" value="7">
<label for="type-7">Blank Paperwork</label> <label for="type-7">Generic Attachment</label>
</div> </div>
<div class="checkbox-item"> <div class="checkbox-item">
<input type="checkbox" id="type-10" name="documentTypes" value="10"> <input type="checkbox" id="type-14" name="documentTypes" value="14">
<label for="type-10">Job Invoice</label> <label for="type-14">Job Invoice</label>
</div> </div>
</div> </div>
</div> </div>

8
templates/partials/document_upload_form.html

@ -19,10 +19,10 @@
<label>Document Type:</label> <label>Document Type:</label>
<select class="card-input" id="document-type-1" name="document-type-1"> <select class="card-input" id="document-type-1" name="document-type-1">
<option value="">Select Document Type</option> <option value="">Select Document Type</option>
<option value="01" selected>Job Paperwork</option> <option value="1" selected>Job Paperwork</option>
<option value="02">Job Vendor Bill</option> <option value="2">Job Vendor Bill</option>
<option value="07">Generic Attachment</option> <option value="4">Generic Attachment</option>
<option value="10">Blank Paperwork</option> <option value="7">Blank Paperwork</option>
<option value="14">Job Invoice</option> <option value="14">Job Invoice</option>
</select> </select>
</div> </div>

Loading…
Cancel
Save